Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -3,26 +3,24 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./bracketMatching';
import * as nls from 'vs/nls';
import { RunOnceScheduler } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { RunOnceScheduler } from 'vs/base/common/async';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService';
import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService';
const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.'));
@@ -73,9 +71,9 @@ type Brackets = [Range, Range];
class BracketsData {
public readonly position: Position;
public readonly brackets: Brackets;
public readonly brackets: Brackets | null;
constructor(position: Position, brackets: Brackets) {
constructor(position: Position, brackets: Brackets | null) {
this.position = position;
this.brackets = brackets;
}
@@ -122,6 +120,7 @@ export class BracketMatchingController extends Disposable implements editorCommo
this._updateBracketsSoon.schedule();
}));
this._register(editor.onDidChangeModel((e) => {
this._lastBracketsData = [];
this._decorations = [];
this._updateBracketsSoon.schedule();
}));
@@ -144,17 +143,17 @@ export class BracketMatchingController extends Disposable implements editorCommo
}
public jumpToBracket(): void {
const model = this._editor.getModel();
if (!model) {
if (!this._editor.hasModel()) {
return;
}
let newSelections = this._editor.getSelections().map(selection => {
const model = this._editor.getModel();
const newSelections = this._editor.getSelections().map(selection => {
const position = selection.getStartPosition();
// find matching brackets if position is on a bracket
const brackets = model.matchBracket(position);
let newCursorPosition: Position = null;
let newCursorPosition: Position | null = null;
if (brackets) {
if (brackets[0].containsPosition(position)) {
newCursorPosition = brackets[1].getStartPosition();
@@ -180,20 +179,19 @@ export class BracketMatchingController extends Disposable implements editorCommo
}
public selectToBracket(): void {
const model = this._editor.getModel();
if (!model) {
if (!this._editor.hasModel()) {
return;
}
let newSelections: Selection[] = [];
const model = this._editor.getModel();
const newSelections: Selection[] = [];
this._editor.getSelections().forEach(selection => {
const position = selection.getStartPosition();
let brackets = model.matchBracket(position);
let openBracket: Position = null;
let closeBracket: Position = null;
let openBracket: Position | null = null;
let closeBracket: Position | null = null;
if (!brackets) {
const nextBracket = model.findNextBracket(position);
@@ -234,7 +232,6 @@ export class BracketMatchingController extends Disposable implements editorCommo
className: 'bracket-match',
overviewRuler: {
color: themeColorFromId(overviewRulerBracketMatchForeground),
darkColor: themeColorFromId(overviewRulerBracketMatchForeground),
position: OverviewRulerLane.Center
}
});
@@ -258,14 +255,14 @@ export class BracketMatchingController extends Disposable implements editorCommo
}
private _recomputeBrackets(): void {
const model = this._editor.getModel();
if (!model) {
if (!this._editor.hasModel()) {
// no model => no brackets!
this._lastBracketsData = [];
this._lastVersionId = 0;
return;
}
const model = this._editor.getModel();
const versionId = model.getVersionId();
let previousData: BracketsData[] = [];
if (this._lastVersionId === versionId) {
@@ -316,11 +313,11 @@ registerEditorContribution(BracketMatchingController);
registerEditorAction(SelectToBracketAction);
registerEditorAction(JumpToBracketAction);
registerThemingParticipant((theme, collector) => {
let bracketMatchBackground = theme.getColor(editorBracketMatchBackground);
const bracketMatchBackground = theme.getColor(editorBracketMatchBackground);
if (bracketMatchBackground) {
collector.addRule(`.monaco-editor .bracket-match { background-color: ${bracketMatchBackground}; }`);
}
let bracketMatchBorder = theme.getColor(editorBracketMatchBorder);
const bracketMatchBorder = theme.getColor(editorBracketMatchBorder);
if (bracketMatchBorder) {
collector.addRule(`.monaco-editor .bracket-match { border: 1px solid ${bracketMatchBorder}; }`);
}

View File

@@ -2,17 +2,15 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/bracketMatching';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
suite('bracket matching', () => {
class BracketMode extends MockMode {

View File

@@ -2,14 +2,13 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IActionOptions, registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { MoveCaretCommand } from './moveCaretCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/moveCaretCommand';
class MoveCaretAction extends EditorAction {
@@ -22,6 +21,9 @@ class MoveCaretAction extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
let commands: ICommand[] = [];
let selections = editor.getSelections();

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/moveCaretCommand';

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { isLowSurrogate, isHighSurrogate } from 'vs/base/common/strings';
import { isHighSurrogate, isLowSurrogate } from 'vs/base/common/strings';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel } from 'vs/editor/common/model';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -75,6 +74,10 @@ class TransposeLettersAction extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
let model = editor.getModel();
let commands: ICommand[] = [];
let selections = editor.getSelections();

View File

@@ -3,21 +3,19 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./clipboard';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as browser from 'vs/base/browser/browser';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { registerEditorAction, IActionOptions, EditorAction, ICommandKeybindingsOptions } from 'vs/editor/browser/editorExtensions';
import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { EditorAction, IActionOptions, ICommandKeybindingsOptions, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
@@ -61,7 +59,7 @@ abstract class ExecCommandAction extends EditorAction {
class ExecCommandCutAction extends ExecCommandAction {
constructor() {
let kbOpts: ICommandKeybindingsOptions = {
let kbOpts: ICommandKeybindingsOptions | null = {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] },
@@ -92,6 +90,10 @@ class ExecCommandCutAction extends ExecCommandAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard;
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
@@ -105,7 +107,7 @@ class ExecCommandCutAction extends ExecCommandAction {
class ExecCommandCopyAction extends ExecCommandAction {
constructor() {
let kbOpts: ICommandKeybindingsOptions = {
let kbOpts: ICommandKeybindingsOptions | null = {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] },
@@ -137,11 +139,21 @@ class ExecCommandCopyAction extends ExecCommandAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard;
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
return;
}
// Prevent copying an empty line by accident
if (editor.getSelections().length === 1 && editor.getSelection().isEmpty()) {
if (editor.getModel().getLineFirstNonWhitespaceColumn(editor.getSelection().positionLineNumber) === 0) {
return;
}
}
super.run(accessor, editor);
}
@@ -150,7 +162,7 @@ class ExecCommandCopyAction extends ExecCommandAction {
class ExecCommandPasteAction extends ExecCommandAction {
constructor() {
let kbOpts: ICommandKeybindingsOptions = {
let kbOpts: ICommandKeybindingsOptions | null = {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
@@ -192,13 +204,17 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: null,
primary: 0,
weight: KeybindingWeight.EditorContrib
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard;
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {

View File

@@ -3,11 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { flatten, isFalsyOrEmpty, mergeSort } from 'vs/base/common/arrays';
import { asWinJsPromise } from 'vs/base/common/async';
import { flatten, mergeSort, isNonEmptyArray } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
@@ -22,21 +21,43 @@ export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Sele
trigger: trigger && trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic
};
const promises = CodeActionProviderRegistry.all(model).map(support => {
return asWinJsPromise(token => support.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => {
if (!Array.isArray(providedCodeActions)) {
return [];
}
return providedCodeActions.filter(action => isValidAction(trigger && trigger.filter, action));
}, (err): CodeAction[] => {
if (isPromiseCanceledError(err)) {
throw err;
const promises = CodeActionProviderRegistry.all(model)
.filter(provider => {
if (!provider.providedCodeActionKinds) {
return true;
}
onUnexpectedExternalError(err);
return [];
// Avoid calling providers that we know will not return code actions of interest
return provider.providedCodeActionKinds.some(providedKind => {
// Filter out actions by kind
// The provided kind can be either a subset of a superset of the filtered kind
if (trigger && trigger.filter && trigger.filter.kind && !(trigger.filter.kind.contains(providedKind) || new CodeActionKind(providedKind).contains(trigger.filter.kind.value))) {
return false;
}
// Don't return source actions unless they are explicitly requested
if (trigger && CodeActionKind.Source.contains(providedKind) && (!trigger.filter || !trigger.filter.includeSourceActions)) {
return false;
}
return true;
});
})
.map(support => {
return Promise.resolve(support.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => {
if (!Array.isArray(providedCodeActions)) {
return [];
}
return providedCodeActions.filter(action => isValidAction(trigger && trigger.filter, action));
}, (err): CodeAction[] => {
if (isPromiseCanceledError(err)) {
throw err;
}
onUnexpectedExternalError(err);
return [];
});
});
});
return Promise.all(promises)
.then(flatten)
@@ -44,17 +65,17 @@ export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Sele
}
function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction): boolean {
if (!action) {
return false;
}
return action && isValidActionKind(filter, action.kind);
}
function isValidActionKind(filter: CodeActionFilter | undefined, kind: string | undefined): boolean {
// Filter out actions by kind
if (filter && filter.kind && (!action.kind || !filter.kind.contains(action.kind))) {
if (filter && filter.kind && (!kind || !filter.kind.contains(kind))) {
return false;
}
// Don't return source actions unless they are explicitly requested
if (action.kind && CodeActionKind.Source.contains(action.kind) && (!filter || !filter.includeSourceActions)) {
if (kind && CodeActionKind.Source.contains(kind) && (!filter || !filter.includeSourceActions)) {
return false;
}
@@ -62,15 +83,13 @@ function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction)
}
function codeActionsComparator(a: CodeAction, b: CodeAction): number {
const aHasDiags = !isFalsyOrEmpty(a.diagnostics);
const bHasDiags = !isFalsyOrEmpty(b.diagnostics);
if (aHasDiags) {
if (bHasDiags) {
if (isNonEmptyArray(a.diagnostics)) {
if (isNonEmptyArray(b.diagnostics)) {
return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);
} else {
return -1;
}
} else if (bHasDiags) {
} else if (isNonEmptyArray(b.diagnostics)) {
return 1;
} else {
return 0; // both have no diagnostics

View File

@@ -7,7 +7,6 @@ import { CancelablePromise } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
@@ -94,7 +93,7 @@ export class QuickFixController implements IEditorContribution {
// Triggered for specific scope
// Apply if we only have one action or requested autoApply, otherwise show menu
e.actions.then(fixes => {
if (e.trigger.autoApply === CodeActionAutoApply.First || (e.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) {
if (fixes.length > 0 && e.trigger.autoApply === CodeActionAutoApply.First || (e.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) {
this._onApplyCodeAction(fixes[0]);
} else {
this._codeActionContextMenu.show(e.actions, e.position);
@@ -124,7 +123,7 @@ export class QuickFixController implements IEditorContribution {
}
private _handleLightBulbSelect(coords: { x: number, y: number }): void {
if (this._lightBulbWidget.model.actions) {
if (this._lightBulbWidget.model && this._lightBulbWidget.model.actions) {
this._codeActionContextMenu.show(this._lightBulbWidget.model.actions, coords);
}
}
@@ -144,8 +143,8 @@ export class QuickFixController implements IEditorContribution {
this._lightBulbWidget.title = title;
}
private _onApplyCodeAction(action: CodeAction): TPromise<void> {
return TPromise.wrap(applyCodeAction(action, this._bulkEditService, this._commandService, this._editor));
private _onApplyCodeAction(action: CodeAction): Promise<void> {
return applyCodeAction(action, this._bulkEditService, this._commandService, this._editor);
}
}

View File

@@ -3,11 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { CancelablePromise, createCancelablePromise, TimeoutTimer } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
@@ -24,22 +23,24 @@ export const SUPPORTED_CODE_ACTIONS = new RawContextKey<string>('supportedCodeAc
export class CodeActionOracle {
private _disposables: IDisposable[] = [];
private readonly _autoTriggerTimer = new TimeoutTimer();
constructor(
private _editor: ICodeEditor,
private readonly _markerService: IMarkerService,
private _signalChange: (e: CodeActionsComputeEvent) => any,
delay: number = 250,
private readonly _delay: number = 250,
private readonly _progressService?: IProgressService,
) {
this._disposables.push(
debounceEvent(this._markerService.onMarkerChanged, (last, cur) => last ? last.concat(cur) : cur, delay / 2)(e => this._onMarkerChanges(e)),
debounceEvent(this._editor.onDidChangeCursorPosition, (last, cur) => cur, delay)(e => this._onCursorChange())
this._markerService.onMarkerChanged(e => this._onMarkerChanges(e)),
this._editor.onDidChangeCursorPosition(() => this._onCursorChange()),
);
}
dispose(): void {
this._disposables = dispose(this._disposables);
this._autoTriggerTimer.cancel();
}
trigger(trigger: CodeActionTrigger) {
@@ -48,21 +49,29 @@ export class CodeActionOracle {
}
private _onMarkerChanges(resources: URI[]): void {
const { uri } = this._editor.getModel();
for (const resource of resources) {
if (resource.toString() === uri.toString()) {
const model = this._editor.getModel();
if (!model) {
return;
}
if (resources.some(resource => resource.toString() === model.uri.toString())) {
this._autoTriggerTimer.cancelAndSet(() => {
this.trigger({ type: 'auto' });
return;
}
}, this._delay);
}
}
private _onCursorChange(): void {
this.trigger({ type: 'auto' });
this._autoTriggerTimer.cancelAndSet(() => {
this.trigger({ type: 'auto' });
}, this._delay);
}
private _getRangeOfMarker(selection: Selection): Range {
private _getRangeOfMarker(selection: Selection): Range | undefined {
const model = this._editor.getModel();
if (!model) {
return undefined;
}
for (const marker of this._markerService.read({ resource: model.uri })) {
if (Range.intersectRanges(marker, selection)) {
return Range.lift(marker);
@@ -72,9 +81,12 @@ export class CodeActionOracle {
}
private _getRangeOfSelectionUnlessWhitespaceEnclosed(trigger: CodeActionTrigger): Selection | undefined {
if (!this._editor.hasModel()) {
return undefined;
}
const model = this._editor.getModel();
const selection = this._editor.getSelection();
if (selection.isEmpty() && !(trigger.filter && trigger.filter.includeSourceActions)) {
if (selection.isEmpty() && trigger.type === 'auto') {
const { lineNumber, column } = selection.getPosition();
const line = model.getLineContent(lineNumber);
if (line.length === 0) {
@@ -97,7 +109,7 @@ export class CodeActionOracle {
}
}
}
return selection;
return selection ? selection : undefined;
}
private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): Thenable<CodeAction[] | undefined> {
@@ -109,15 +121,26 @@ export class CodeActionOracle {
position: undefined,
actions: undefined,
});
return TPromise.as(undefined);
return Promise.resolve(undefined);
} else {
const model = this._editor.getModel();
if (!model) {
// cancel
this._signalChange({
trigger,
rangeOrSelection: undefined,
position: undefined,
actions: undefined,
});
return Promise.resolve(undefined);
}
const markerRange = this._getRangeOfMarker(selection);
const position = markerRange ? markerRange.getStartPosition() : selection.getStartPosition();
const actions = createCancelablePromise(token => getCodeActions(model, selection, trigger, token));
if (this._progressService && trigger.type === 'manual') {
this._progressService.showWhile(TPromise.wrap(actions), 250);
this._progressService.showWhile(actions, 250);
}
this._signalChange({
@@ -133,16 +156,16 @@ export class CodeActionOracle {
export interface CodeActionsComputeEvent {
trigger: CodeActionTrigger;
rangeOrSelection: Range | Selection;
position: Position;
actions: CancelablePromise<CodeAction[]>;
rangeOrSelection: Range | Selection | undefined;
position: Position | undefined;
actions: CancelablePromise<CodeAction[]> | undefined;
}
export class CodeActionModel {
private _editor: ICodeEditor;
private _markerService: IMarkerService;
private _codeActionOracle: CodeActionOracle;
private _codeActionOracle?: CodeActionOracle;
private _onDidChangeFixes = new Emitter<CodeActionsComputeEvent>();
private _disposables: IDisposable[] = [];
private readonly _supportedCodeActions: IContextKey<string>;
@@ -177,12 +200,13 @@ export class CodeActionModel {
this._onDidChangeFixes.fire(undefined);
}
if (this._editor.getModel()
&& CodeActionProviderRegistry.has(this._editor.getModel())
const model = this._editor.getModel();
if (model
&& CodeActionProviderRegistry.has(model)
&& !this._editor.getConfiguration().readOnly) {
const supportedActions: string[] = [];
for (const provider of CodeActionProviderRegistry.all(this._editor.getModel())) {
for (const provider of CodeActionProviderRegistry.all(model)) {
if (Array.isArray(provider.providedCodeActionKinds)) {
supportedActions.push(...provider.providedCodeActionKinds);
}
@@ -201,6 +225,6 @@ export class CodeActionModel {
if (this._codeActionOracle) {
return this._codeActionOracle.trigger(trigger);
}
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
}

View File

@@ -23,7 +23,7 @@ export class CodeActionKind {
}
}
export enum CodeActionAutoApply {
export const enum CodeActionAutoApply {
IfSingle = 1,
First = 2,
Never = 3

View File

@@ -8,7 +8,6 @@ import { Action } from 'vs/base/common/actions';
import { always } from 'vs/base/common/async';
import { canceled } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { ScrollType } from 'vs/editor/common/editorCommon';
@@ -25,12 +24,12 @@ export class CodeActionContextMenu {
constructor(
private readonly _editor: ICodeEditor,
private readonly _contextMenuService: IContextMenuService,
private readonly _onApplyCodeAction: (action: CodeAction) => TPromise<any>
private readonly _onApplyCodeAction: (action: CodeAction) => Promise<any>
) { }
show(fixes: Thenable<CodeAction[]>, at: { x: number; y: number } | Position) {
const actions = fixes.then(value => {
const actionsPromise = fixes ? fixes.then(value => {
return value.map(action => {
return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => {
return always(
@@ -41,24 +40,26 @@ export class CodeActionContextMenu {
}).then(actions => {
if (!this._editor.getDomNode()) {
// cancel when editor went off-dom
return TPromise.wrapError<any>(canceled());
return Promise.reject(canceled());
}
return actions;
});
}) : Promise.resolve([] as Action[]);
this._contextMenuService.showContextMenu({
getAnchor: () => {
if (Position.isIPosition(at)) {
at = this._toCoords(at);
}
return at;
},
getActions: () => TPromise.wrap(actions),
onHide: () => {
this._visible = false;
this._editor.focus();
},
autoSelectFirstItem: true
actionsPromise.then(actions => {
this._contextMenuService.showContextMenu({
getAnchor: () => {
if (Position.isIPosition(at)) {
at = this._toCoords(at);
}
return at;
},
getActions: () => actions,
onHide: () => {
this._visible = false;
this._editor.focus();
},
autoSelectFirstItem: true
});
});
}

View File

@@ -11,7 +11,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import 'vs/css!./lightBulbWidget';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { TextModel } from 'vs/editor/common/model/textModel';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { CodeActionsComputeEvent } from './codeActionModel';
export class LightBulbWidget implements IDisposable, IContentWidget {
@@ -25,8 +24,8 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
readonly onClick: Event<{ x: number, y: number }> = this._onClick.event;
private _position: IContentWidgetPosition;
private _model: CodeActionsComputeEvent;
private _position: IContentWidgetPosition | null;
private _model: CodeActionsComputeEvent | null;
private _futureFixes = new CancellationTokenSource();
constructor(editor: ICodeEditor) {
@@ -40,7 +39,8 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._futureFixes.cancel()));
this._disposables.push(this._editor.onDidChangeModelContent(_ => {
// cancel when the line in question has been removed
if (this._model && this.model.position.lineNumber >= this._editor.getModel().getLineCount()) {
const editorModel = this._editor.getModel();
if (!this.model || !this.model.position || !editorModel || this.model.position.lineNumber >= editorModel.getLineCount()) {
this._futureFixes.cancel();
}
}));
@@ -53,7 +53,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
const { lineHeight } = this._editor.getConfiguration();
let pad = Math.floor(lineHeight / 3);
if (this._position && this._position.position.lineNumber < this._model.position.lineNumber) {
if (this._position && this._model && this._model.position && this._position.position !== null && this._position.position.lineNumber < this._model.position.lineNumber) {
pad += lineHeight;
}
@@ -96,13 +96,13 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
return this._domNode;
}
getPosition(): IContentWidgetPosition {
getPosition(): IContentWidgetPosition | null {
return this._position;
}
set model(value: CodeActionsComputeEvent) {
set model(value: CodeActionsComputeEvent | null) {
if (this._position && (!value.position || this._position.position.lineNumber !== value.position.lineNumber)) {
if (!value || this._position && (!value.position || this._position.position && this._position.position.lineNumber !== value.position.lineNumber)) {
// hide when getting a 'hide'-request or when currently
// showing on another line
this.hide();
@@ -115,23 +115,23 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
const { token } = this._futureFixes;
this._model = value;
if (!this._model || !this._model.actions) {
return;
}
const selection = this._model.rangeOrSelection;
this._model.actions.then(fixes => {
if (!token.isCancellationRequested && fixes && fixes.length > 0) {
if (selection.isEmpty() && fixes.every(fix => fix.kind && CodeActionKind.Refactor.contains(fix.kind))) {
this.hide();
} else {
this._show();
}
if (!token.isCancellationRequested && fixes && fixes.length > 0 && selection) {
this._show();
} else {
this.hide();
}
}).catch(err => {
}).catch(() => {
this.hide();
});
}
get model(): CodeActionsComputeEvent {
get model(): CodeActionsComputeEvent | null {
return this._model;
}
@@ -148,7 +148,10 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
if (!config.contribInfo.lightbulbEnabled) {
return;
}
const { lineNumber } = this._model.position;
if (!this._model || !this._model.position) {
return;
}
const { lineNumber, column } = this._model.position;
const model = this._editor.getModel();
if (!model) {
return;
@@ -158,13 +161,21 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
const lineContent = model.getLineContent(lineNumber);
const indent = TextModel.computeIndentLevel(lineContent, tabSize);
const lineHasSpace = config.fontInfo.spaceWidth * indent > 22;
const isFolded = (lineNumber) => {
return lineNumber > 2 && this._editor.getTopForLineNumber(lineNumber) === this._editor.getTopForLineNumber(lineNumber - 1);
};
let effectiveLineNumber = lineNumber;
if (!lineHasSpace) {
if (lineNumber > 1) {
if (lineNumber > 1 && !isFolded(lineNumber - 1)) {
effectiveLineNumber -= 1;
} else {
} else if (!isFolded(lineNumber + 1)) {
effectiveLineNumber += 1;
} else if (column * config.fontInfo.spaceWidth < 22) {
// cannot show lightbulb above/below and showing
// it inline would overlay the cursor...
this.hide();
return;
}
}

View File

@@ -2,11 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { CodeAction, CodeActionContext, CodeActionProvider, CodeActionProviderRegistry, Command, LanguageIdentifier, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes';
@@ -194,4 +192,27 @@ suite('CodeAction', () => {
assert.strictEqual(actions[0].title, 'a');
}
});
test('getCodeActions should not invoke code action providers filtered out by providedCodeActionKinds', async function () {
let wasInvoked = false;
const provider = new class implements CodeActionProvider {
provideCodeActions() {
wasInvoked = true;
return [];
}
providedCodeActionKinds = [CodeActionKind.Refactor.value];
};
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: 'auto',
filter: {
kind: CodeActionKind.QuickFix
}
});
assert.strictEqual(actions.length, 0);
assert.strictEqual(wasInvoked, false);
});
});

View File

@@ -5,7 +5,7 @@
import * as assert from 'assert';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Selection } from 'vs/editor/common/core/selection';
import { TextModel } from 'vs/editor/common/model/textModel';
@@ -135,21 +135,33 @@ suite('CodeAction', () => {
editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 });
});
});
// // case 2 - selection over multiple lines & manual trigger -> lightbulb
// await new TPromise(resolve => {
test('Orcale -> should only auto trigger once for cursor and marker update right after each other', done => {
const reg = CodeActionProviderRegistry.register(languageIdentifier.language, testProvider);
disposables.push(reg);
// editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 });
let triggerCount = 0;
const oracle = new CodeActionOracle(editor, markerService, e => {
assert.equal(e.trigger.type, 'auto');
++triggerCount;
// let oracle = new QuickFixOracle(editor, markerService, e => {
// assert.equal(e.type, 'manual');
// assert.ok(e.range.equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 }));
// give time for second trigger before completing test
setTimeout(() => {
oracle.dispose();
assert.strictEqual(triggerCount, 1);
done();
}, 50);
}, 5 /*delay*/);
// oracle.dispose();
// resolve(null);
// }, 5);
markerService.changeOne('fake', uri, [{
startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,
message: 'error',
severity: 1,
code: '',
source: ''
}]);
// oracle.trigger('manual');
// });
editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 });
});
});

View File

@@ -3,16 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import { mergeSort } from 'vs/base/common/arrays';
import URI from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { ITextModel } from 'vs/editor/common/model';
import { CodeLensProvider, CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
export interface ICodeLensData {
symbol: ICodeLensSymbol;
@@ -75,8 +73,8 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) {
for (const item of value) {
if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) {
result.push(item.symbol);
} else if (itemResolveCount-- > 0) {
resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol)));
} else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) {
resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol || item.symbol)));
}
}

View File

@@ -3,11 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async';
import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
@@ -15,10 +13,10 @@ import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOption
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import { ICodeLensData, getCodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { getCodeLensData, ICodeLensData } from './codelens';
export class CodeLensContribution implements editorCommon.IEditorContribution {
@@ -181,7 +179,24 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._disposeAllLenses(null, null);
}
}));
this._localToDispose.push(this._editor.onDidChangeConfiguration(e => {
if (e.fontInfo) {
for (const lens of this._lenses) {
lens.updateHeight();
}
}
}));
this._localToDispose.push(this._editor.onMouseUp(e => {
if (e.target.type === editorBrowser.MouseTargetType.CONTENT_WIDGET && e.target.element.tagName === 'A') {
for (const lens of this._lenses) {
let command = lens.getCommand(e.target.element as HTMLLinkElement);
if (command) {
this._commandService.executeCommand(command.id, ...command.arguments).catch(err => this._notificationService.error(err));
break;
}
}
}
}));
scheduler.schedule();
}
@@ -238,7 +253,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
groupsIndex++;
codeLensIndex++;
} else {
this._lenses.splice(codeLensIndex, 0, new CodeLens(groups[groupsIndex], this._editor, helper, accessor, this._commandService, this._notificationService, () => this._detectVisibleLenses.schedule()));
this._lenses.splice(codeLensIndex, 0, new CodeLens(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule()));
codeLensIndex++;
groupsIndex++;
}
@@ -252,7 +267,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
// Create extra symbols
while (groupsIndex < groups.length) {
this._lenses.push(new CodeLens(groups[groupsIndex], this._editor, helper, accessor, this._commandService, this._notificationService, () => this._detectVisibleLenses.schedule()));
this._lenses.push(new CodeLens(groups[groupsIndex], this._editor, helper, accessor, () => this._detectVisibleLenses.schedule()));
groupsIndex++;
}

View File

@@ -3,23 +3,19 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./codelensWidget';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { format, escape } from 'vs/base/common/strings';
import * as dom from 'vs/base/browser/dom';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { Range } from 'vs/editor/common/core/range';
import { ICodeLensSymbol, Command } from 'vs/editor/common/modes';
import { coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays';
import { escape, format } from 'vs/base/common/strings';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { ICodeLensData } from './codelens';
import { Range } from 'vs/editor/common/core/range';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { Command, ICodeLensSymbol } from 'vs/editor/common/modes';
import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ICodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IModelDeltaDecoration, IModelDecorationsChangeAccessor, ITextModel } from 'vs/editor/common/model';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
class CodeLensViewZone implements editorBrowser.IViewZone {
@@ -61,7 +57,6 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
private readonly _id: string;
private readonly _domNode: HTMLElement;
private readonly _disposables: IDisposable[] = [];
private readonly _editor: editorBrowser.ICodeEditor;
private _widgetPosition: editorBrowser.IContentWidgetPosition;
@@ -69,11 +64,8 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
constructor(
editor: editorBrowser.ICodeEditor,
symbolRange: Range,
commandService: ICommandService,
notificationService: INotificationService
symbolRange: Range
) {
this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool);
this._editor = editor;
@@ -83,35 +75,16 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
this._domNode.innerHTML = '&nbsp;';
dom.addClass(this._domNode, 'codelens-decoration');
dom.addClass(this._domNode, 'invisible-cl');
this._updateHeight();
this._disposables.push(this._editor.onDidChangeConfiguration(e => e.fontInfo && this._updateHeight()));
this._disposables.push(dom.addDisposableListener(this._domNode, 'click', e => {
let element = <HTMLElement>e.target;
if (element.tagName === 'A' && element.id) {
let command = this._commands[element.id];
if (command) {
editor.focus();
commandService.executeCommand(command.id, ...command.arguments).done(undefined, err => {
notificationService.error(err);
});
}
}
}));
this.updateHeight();
this.updateVisibility();
}
dispose(): void {
dispose(this._disposables);
}
private _updateHeight(): void {
updateHeight(): void {
const { fontInfo, lineHeight } = this._editor.getConfiguration();
this._domNode.style.height = `${Math.round(lineHeight * 1.1)}px`;
this._domNode.style.lineHeight = `${lineHeight}px`;
this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * .9)}px`;
this._domNode.style.paddingRight = `${Math.round(fontInfo.fontSize * .45)}px`;
this._domNode.innerHTML = '&nbsp;';
}
@@ -124,7 +97,8 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
withCommands(symbols: ICodeLensSymbol[]): void {
this._commands = Object.create(null);
if (!symbols || !symbols.length) {
symbols = coalesce(symbols);
if (isFalsyOrEmpty(symbols)) {
this._domNode.innerHTML = 'no commands';
return;
}
@@ -147,6 +121,12 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
this._editor.layoutContentWidget(this);
}
getCommand(link: HTMLLinkElement): Command | undefined {
return link.parentElement === this._domNode
? this._commands[link.id]
: undefined;
}
getId(): string {
return this._id;
}
@@ -220,8 +200,7 @@ export class CodeLens {
editor: editorBrowser.ICodeEditor,
helper: CodeLensHelper,
viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor,
commandService: ICommandService, notificationService: INotificationService,
updateCallabck: Function
updateCallback: Function
) {
this._editor = editor;
this._data = data;
@@ -243,8 +222,8 @@ export class CodeLens {
}
});
this._contentWidget = new CodeLensContentWidget(editor, range, commandService, notificationService);
this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallabck);
this._contentWidget = new CodeLensContentWidget(editor, range);
this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallback);
this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone);
this._editor.addContentWidget(this._contentWidget);
@@ -258,8 +237,6 @@ export class CodeLens {
viewZoneChangeAccessor.removeZone(this._viewZoneId);
}
this._editor.removeContentWidget(this._contentWidget);
this._contentWidget.dispose();
}
isValid(): boolean {
@@ -301,6 +278,14 @@ export class CodeLens {
this._contentWidget.withCommands(symbols);
}
updateHeight(): void {
this._contentWidget.updateHeight();
}
getCommand(link: HTMLLinkElement): Command | undefined {
return this._contentWidget.getCommand(link);
}
getLineNumber(): number {
const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]);
if (range) {
@@ -323,11 +308,11 @@ export class CodeLens {
}
registerThemingParticipant((theme, collector) => {
let codeLensForeground = theme.getColor(editorCodeLensForeground);
const codeLensForeground = theme.getColor(editorCodeLensForeground);
if (codeLensForeground) {
collector.addRule(`.monaco-editor .codelens-decoration { color: ${codeLensForeground}; }`);
}
let activeLinkForeground = theme.getColor(editorActiveLinkForeground);
const activeLinkForeground = theme.getColor(editorActiveLinkForeground);
if (activeLinkForeground) {
collector.addRule(`.monaco-editor .codelens-decoration > a:hover { color: ${activeLinkForeground} !important; }`);
}

View File

@@ -3,17 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes';
import { asWinJsPromise } from 'vs/base/common/async';
import { ITextModel } from 'vs/editor/common/model';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Range, IRange } from 'vs/editor/common/core/range';
import { illegalArgument } from 'vs/base/common/errors';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
export interface IColorData {
@@ -35,7 +32,7 @@ export function getColors(model: ITextModel, token: CancellationToken): Promise<
return Promise.all(promises).then(() => colors);
}
export function getColorPresentations(model: ITextModel, colorInfo: IColorInformation, provider: DocumentColorProvider, token: CancellationToken): Promise<IColorPresentation[]> {
export function getColorPresentations(model: ITextModel, colorInfo: IColorInformation, provider: DocumentColorProvider, token: CancellationToken): Promise<IColorPresentation[] | null | undefined> {
return Promise.resolve(provider.provideColorPresentations(model, colorInfo, token));
}
@@ -53,7 +50,7 @@ registerLanguageCommand('_executeDocumentColorProvider', function (accessor, arg
const rawCIs: { range: IRange, color: [number, number, number, number] }[] = [];
const providers = ColorProviderRegistry.ordered(model).reverse();
const promises = providers.map(provider => asWinJsPromise(token => provider.provideDocumentColors(model, token)).then(result => {
const promises = providers.map(provider => Promise.resolve(provider.provideDocumentColors(model, CancellationToken.None)).then(result => {
if (Array.isArray(result)) {
for (let ci of result) {
rawCIs.push({ range: ci.range, color: [ci.color.red, ci.color.green, ci.color.blue, ci.color.alpha] });
@@ -61,7 +58,7 @@ registerLanguageCommand('_executeDocumentColorProvider', function (accessor, arg
}
}));
return TPromise.join(promises).then(() => rawCIs);
return Promise.all(promises).then(() => rawCIs);
});
@@ -85,10 +82,10 @@ registerLanguageCommand('_executeColorPresentationProvider', function (accessor,
const presentations: IColorPresentation[] = [];
const providers = ColorProviderRegistry.ordered(model).reverse();
const promises = providers.map(provider => asWinJsPromise(token => provider.provideColorPresentations(model, colorInfo, token)).then(result => {
const promises = providers.map(provider => Promise.resolve(provider.provideColorPresentations(model, colorInfo, CancellationToken.None)).then(result => {
if (Array.isArray(result)) {
presentations.push(...result);
}
}));
return TPromise.join(promises).then(() => presentations);
return Promise.all(promises).then(() => presentations);
});

View File

@@ -3,21 +3,22 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise, TimeoutTimer, createCancelablePromise } from 'vs/base/common/async';
import { RGBA } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
import { hash } from 'vs/base/common/hash';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { ColorProviderRegistry } from 'vs/editor/common/modes';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { getColors, IColorData } from 'vs/editor/contrib/colorPicker/color';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IModelDeltaDecoration } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ColorProviderRegistry } from 'vs/editor/common/modes';
import { IColorData, getColors } from 'vs/editor/contrib/colorPicker/color';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
const MAX_DECORATORS = 500;
@@ -29,8 +30,8 @@ export class ColorDetector implements IEditorContribution {
private _globalToDispose: IDisposable[] = [];
private _localToDispose: IDisposable[] = [];
private _computePromise: CancelablePromise<IColorData[]>;
private _timeoutTimer: TimeoutTimer;
private _computePromise: CancelablePromise<IColorData[]> | null;
private _timeoutTimer: TimeoutTimer | null;
private _decorationsIds: string[] = [];
private _colorDatas = new Map<string, IColorData>();
@@ -107,11 +108,8 @@ export class ColorDetector implements IEditorContribution {
return;
}
const model = this._editor.getModel();
// if (!model) {
// return;
// }
if (!ColorProviderRegistry.has(model)) {
if (!model || !ColorProviderRegistry.has(model)) {
return;
}
@@ -128,7 +126,13 @@ export class ColorDetector implements IEditorContribution {
}
private beginCompute(): void {
this._computePromise = createCancelablePromise(token => getColors(this._editor.getModel(), token));
this._computePromise = createCancelablePromise(token => {
const model = this._editor.getModel();
if (!model) {
return Promise.resolve([]);
}
return getColors(model, token);
});
this._computePromise.then((colorInfos) => {
this.updateDecorations(colorInfos);
this.updateColorDecorators(colorInfos);
@@ -166,7 +170,7 @@ export class ColorDetector implements IEditorContribution {
}
private updateColorDecorators(colorData: IColorData[]): void {
let decorations = [];
let decorations: IModelDeltaDecoration[] = [];
let newDecorationsTypes: { [key: string]: boolean } = {};
for (let i = 0; i < colorData.length && decorations.length < MAX_DECORATORS; i++) {
@@ -225,7 +229,12 @@ export class ColorDetector implements IEditorContribution {
}
getColorData(position: Position): IColorData | null {
const decorations = this._editor.getModel()
const model = this._editor.getModel();
if (!model) {
return null;
}
const decorations = model
.getDecorationsInRange(Range.fromPositions(position, position))
.filter(d => this._colorDatas.has(d.id));

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { IColorPresentation } from 'vs/editor/common/modes';
export class ColorPickerModel {

View File

@@ -4,16 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./colorPicker';
import { Event, Emitter } from 'vs/base/common/event';
import { Widget } from 'vs/base/browser/ui/widget';
import * as dom from 'vs/base/browser/dom';
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
import { Disposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
import { Color, RGBA, HSVA } from 'vs/base/common/color';
import { Widget } from 'vs/base/browser/ui/widget';
import { Color, HSVA, RGBA } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
import { editorHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
const $ = dom.$;
@@ -126,7 +126,7 @@ class SaturationBox extends Disposable {
private width: number;
private height: number;
private monitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
private monitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData> | null;
private _onDidChange = new Emitter<{ s: number, v: number }>();
readonly onDidChange: Event<{ s: number, v: number }> = this._onDidChange.event;
@@ -168,8 +168,10 @@ class SaturationBox extends Disposable {
const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => {
this._onColorFlushed.fire();
mouseUpListener.dispose();
this.monitor.stopMonitoring(true);
this.monitor = null;
if (this.monitor) {
this.monitor.stopMonitoring(true);
this.monitor = null;
}
}, true);
}
@@ -195,7 +197,7 @@ class SaturationBox extends Disposable {
private paint(): void {
const hsva = this.model.color.hsva;
const saturatedColor = new Color(new HSVA(hsva.h, 1, 1, 1));
const ctx = this.canvas.getContext('2d');
const ctx = this.canvas.getContext('2d')!;
const whiteGradient = ctx.createLinearGradient(0, 0, this.canvas.width, 0);
whiteGradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
@@ -207,7 +209,7 @@ class SaturationBox extends Disposable {
blackGradient.addColorStop(1, 'rgba(0, 0, 0, 1)');
ctx.rect(0, 0, this.canvas.width, this.canvas.height);
ctx.fillStyle = Color.Format.CSS.format(saturatedColor);
ctx.fillStyle = Color.Format.CSS.format(saturatedColor)!;
ctx.fill();
ctx.fillStyle = whiteGradient;
ctx.fill();

View File

@@ -2,21 +2,20 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CharCode } from 'vs/base/common/charCode';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ICommentsConfiguration, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { CharCode } from 'vs/base/common/charCode';
import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
export class BlockCommentCommand implements editorCommon.ICommand {
private _selection: Selection;
private _usedEndToken: string;
private _usedEndToken: string | null;
constructor(selection: Selection) {
this._selection = selection;
@@ -54,7 +53,7 @@ export class BlockCommentCommand implements editorCommon.ICommand {
return true;
}
private _createOperationsForBlockComment(selection: Range, config: ICommentsConfiguration, model: ITextModel, builder: editorCommon.IEditOperationBuilder): void {
private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: editorCommon.IEditOperationBuilder): void {
const startLineNumber = selection.startLineNumber;
const startColumn = selection.startColumn;
const endLineNumber = selection.endLineNumber;
@@ -63,9 +62,6 @@ export class BlockCommentCommand implements editorCommon.ICommand {
const startLineText = model.getLineContent(startLineNumber);
const endLineText = model.getLineContent(endLineNumber);
let startToken = config.blockCommentStartToken;
let endToken = config.blockCommentEndToken;
let startTokenIndex = startLineText.lastIndexOf(startToken, startColumn - 1 + startToken.length);
let endTokenIndex = endLineText.indexOf(endToken, endColumn - 1 - endToken.length);
@@ -180,9 +176,7 @@ export class BlockCommentCommand implements editorCommon.ICommand {
return;
}
this._createOperationsForBlockComment(
this._selection, config, model, builder
);
this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, model, builder);
}
public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection {

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, IActionOptions, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { BlockCommentCommand } from './blockCommentCommand';
import { LineCommentCommand, Type } from './lineCommentCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
import { LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand';
import { MenuId } from 'vs/platform/actions/common/actions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
abstract class CommentLineAction extends EditorAction {
@@ -25,11 +24,11 @@ abstract class CommentLineAction extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let model = editor.getModel();
if (!model) {
if (!editor.hasModel()) {
return;
}
let model = editor.getModel();
let commands: ICommand[] = [];
let selections = editor.getSelections();
let opts = model.getOptions();
@@ -123,9 +122,12 @@ class BlockCommentAction extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
let commands: ICommand[] = [];
let selections = editor.getSelections();
for (let i = 0; i < selections.length; i++) {
commands.push(new BlockCommentCommand(selections[i]));
}

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CharCode } from 'vs/base/common/charCode';
import * as strings from 'vs/base/common/strings';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BlockCommentCommand } from './blockCommentCommand';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { CharCode } from 'vs/base/common/charCode';
import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
export interface IInsertionPoint {
ignore: boolean;
@@ -27,11 +26,15 @@ export interface ILinePreflightData {
commentStrLength: number;
}
export interface IPreflightData {
supported: boolean;
export interface IPreflightDataSupported {
supported: true;
shouldRemoveComments: boolean;
lines: ILinePreflightData[];
}
export interface IPreflightDataUnsupported {
supported: false;
}
export type IPreflightData = IPreflightDataSupported | IPreflightDataUnsupported;
export interface ISimpleModel {
getLineContent(lineNumber: number): string;
@@ -63,7 +66,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Do an initial pass over the lines and gather info about the line comment string.
* Returns null if any of the lines doesn't support a line comment string.
*/
public static _gatherPreflightCommentStrings(model: ITextModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] {
public static _gatherPreflightCommentStrings(model: ITextModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] | null {
model.tokenizeIfCheap(startLineNumber);
const languageId = model.getLanguageIdAtPosition(startLineNumber, 1);
@@ -171,9 +174,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber);
if (lines === null) {
return {
supported: false,
shouldRemoveComments: false,
lines: null
supported: false
};
}
@@ -183,7 +184,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
/**
* Given a successful analysis, execute either insert line comments, either remove line comments
*/
private _executeLineComments(model: ISimpleModel, builder: editorCommon.IEditOperationBuilder, data: IPreflightData, s: Selection): void {
private _executeLineComments(model: ISimpleModel, builder: editorCommon.IEditOperationBuilder, data: IPreflightDataSupported, s: Selection): void {
let ops: IIdentifiedSingleEditOperation[];
@@ -201,7 +202,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
if (ops[i].range.isEmpty() && ops[i].range.getStartPosition().equals(cursorPosition)) {
const lineContent = model.getLineContent(cursorPosition.lineNumber);
if (lineContent.length + 1 === cursorPosition.column) {
this._deltaColumn = ops[i].text.length;
this._deltaColumn = (ops[i].text || '').length;
}
}
}
@@ -209,7 +210,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
this._selectionId = builder.trackSelection(s);
}
private _attemptRemoveBlockComment(model: ITextModel, s: Selection, startToken: string, endToken: string): IIdentifiedSingleEditOperation[] {
private _attemptRemoveBlockComment(model: ITextModel, s: Selection, startToken: string, endToken: string): IIdentifiedSingleEditOperation[] | null {
let startLineNumber = s.startLineNumber;
let endLineNumber = s.endLineNumber;

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
@@ -59,7 +57,7 @@ suite('Editor Contrib - Block Comment Command', () => {
);
});
test('bug9511', function () {
test('bug9511', () => {
testBlockCommentCommand(
[
'first',

View File

@@ -2,19 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Selection } from 'vs/editor/common/core/selection';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import * as modes from 'vs/editor/common/modes';
import { CommentRule } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { ILinePreflightData, IPreflightData, ISimpleModel, LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
import { CommentMode } from 'vs/editor/test/common/commentMode';
import * as modes from 'vs/editor/common/modes';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { CommentRule } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
suite('Editor Contrib - Line Comment Command', () => {
@@ -84,7 +82,7 @@ suite('Editor Contrib - Line Comment Command', () => {
});
}
test('_analyzeLines', function () {
test('_analyzeLines', () => {
let r: IPreflightData;
r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([
@@ -93,6 +91,9 @@ suite('Editor Contrib - Line Comment Command', () => {
' c',
'\t\td'
]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1);
if (!r.supported) {
throw new Error(`unexpected`);
}
assert.equal(r.shouldRemoveComments, false);
@@ -121,6 +122,9 @@ suite('Editor Contrib - Line Comment Command', () => {
' !@# c',
'\t\t!@#d'
]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1);
if (!r.supported) {
throw new Error(`unexpected`);
}
assert.equal(r.shouldRemoveComments, true);
@@ -149,7 +153,7 @@ suite('Editor Contrib - Line Comment Command', () => {
assert.equal(r.lines[3].commentStrLength, 3);
});
test('_normalizeInsertionPoint', function () {
test('_normalizeInsertionPoint', () => {
const runTest = (mixedArr: any[], tabSize: number, expected: number[], testName: string) => {
const model = createSimpleModel(mixedArr.filter((item, idx) => idx % 2 === 0));

View File

@@ -2,31 +2,25 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { IAction } from 'vs/base/common/actions';
import { ResolvedKeybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { IAction } from 'vs/base/common/actions';
import { KeyCode, KeyMod, ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export interface IPosition {
x: number;
y: number;
}
export class ContextMenuController implements IEditorContribution {
private static readonly ID = 'editor.contrib.contextmenu';
@@ -94,16 +88,16 @@ export class ContextMenuController implements IEditorContribution {
}
// Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position
let forcedPosition: IPosition;
let anchor: IAnchor;
if (e.target.type !== MouseTargetType.TEXTAREA) {
forcedPosition = { x: e.event.posx, y: e.event.posy + 1 };
anchor = { x: e.event.posx - 1, width: 2, y: e.event.posy - 1, height: 2 };
}
// Show the context menu
this.showContextMenu(forcedPosition);
this.showContextMenu(anchor);
}
public showContextMenu(forcedPosition?: IPosition): void {
public showContextMenu(anchor?: IAnchor): void {
if (!this._editor.getConfiguration().contribInfo.contextmenu) {
return; // Context menu is turned off through configuration
}
@@ -118,7 +112,7 @@ export class ContextMenuController implements IEditorContribution {
// Show menu if we have actions to show
if (menuActions.length > 0) {
this._doShowContextMenu(menuActions, forcedPosition);
this._doShowContextMenu(menuActions, anchor);
}
}
@@ -138,7 +132,7 @@ export class ContextMenuController implements IEditorContribution {
return result;
}
private _doShowContextMenu(actions: IAction[], forcedPosition: IPosition = null): void {
private _doShowContextMenu(actions: IAction[], anchor: IAnchor | null = null): void {
// Disable hover
const oldHoverSetting = this._editor.getConfiguration().contribInfo.hover;
@@ -148,8 +142,7 @@ export class ContextMenuController implements IEditorContribution {
}
});
let menuPosition = forcedPosition;
if (!menuPosition) {
if (!anchor) {
// Ensure selection is visible
this._editor.revealPosition(this._editor.getPosition(), ScrollType.Immediate);
@@ -161,17 +154,15 @@ export class ContextMenuController implements IEditorContribution {
const posx = editorCoords.left + cursorCoords.left;
const posy = editorCoords.top + cursorCoords.top + cursorCoords.height;
menuPosition = { x: posx, y: posy };
anchor = { x: posx, y: posy };
}
// Show menu
this._contextMenuIsBeingShownCount++;
this._contextMenuService.showContextMenu({
getAnchor: () => menuPosition,
getAnchor: () => anchor,
getActions: () => {
return TPromise.as(actions);
},
getActions: () => actions,
getActionItem: (action) => {
const keybinding = this._keybindingFor(action);

View File

@@ -2,16 +2,15 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { Selection } from 'vs/editor/common/core/selection';
import { ServicesAccessor, registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
class CursorState {
@@ -48,7 +47,7 @@ export class CursorUndoController extends Disposable implements IEditorContribut
private _isCursorUndo: boolean;
private _undoStack: CursorState[];
private _prevState: CursorState;
private _prevState: CursorState | null;
constructor(editor: ICodeEditor) {
super();
@@ -80,8 +79,8 @@ export class CursorUndoController extends Disposable implements IEditorContribut
}));
}
private _readState(): CursorState {
if (!this._editor.getModel()) {
private _readState(): CursorState | null {
if (!this._editor.hasModel()) {
// no model => no state
return null;
}
@@ -94,10 +93,14 @@ export class CursorUndoController extends Disposable implements IEditorContribut
}
public cursorUndo(): void {
if (!this._editor.hasModel()) {
return;
}
const currState = new CursorState(this._editor.getSelections());
while (this._undoStack.length > 0) {
const prevState = this._undoStack.pop();
const prevState = this._undoStack.pop()!;
if (!prevState.equals(currState)) {
this._isCursorUndo = true;

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./dnd';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -20,6 +18,7 @@ import { DragAndDropCommand } from 'vs/editor/contrib/dnd/dragAndDropCommand';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IModelDeltaDecoration } from 'vs/editor/common/model';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean {
if (isMacintosh) {
@@ -35,7 +34,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
private _editor: ICodeEditor;
private _toUnhook: IDisposable[];
private _dragSelection: Selection;
private _dragSelection: Selection | null;
private _dndDecorationIds: string[];
private _mouseDown: boolean;
private _modiferPressed: boolean;
@@ -54,12 +53,20 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
this._toUnhook.push(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e)));
this._toUnhook.push(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e)));
this._toUnhook.push(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e)));
this._toUnhook.push(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur()));
this._dndDecorationIds = [];
this._mouseDown = false;
this._modiferPressed = false;
this._dragSelection = null;
}
private onEditorBlur() {
this._removeDecoration();
this._dragSelection = null;
this._mouseDown = false;
this._modiferPressed = false;
}
private onEditorKeyDown(e: IKeyboardEvent): void {
if (!this._editor.getConfiguration().dragAndDrop) {
return;
@@ -108,7 +115,8 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
let target = mouseEvent.target;
if (this._dragSelection === null) {
let possibleSelections = this._editor.getSelections().filter(selection => selection.containsPosition(target.position));
const selections = this._editor.getSelections() || [];
let possibleSelections = selections.filter(selection => target.position && selection.containsPosition(target.position));
if (possibleSelections.length === 1) {
this._dragSelection = possibleSelections[0];
} else {
@@ -126,10 +134,12 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
});
}
if (this._dragSelection.containsPosition(target.position)) {
this._removeDecoration();
} else {
this.showAt(target.position);
if (target.position) {
if (this._dragSelection.containsPosition(target.position)) {
this._removeDecoration();
} else {
this.showAt(target.position);
}
}
}
@@ -138,20 +148,24 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column);
if (this._dragSelection === null) {
let newSelections: Selection[] | null = null;
if (mouseEvent.event.shiftKey) {
let primarySelection = this._editor.getSelection();
let { startLineNumber, startColumn } = primarySelection;
this._editor.setSelections([new Selection(startLineNumber, startColumn, newCursorPosition.lineNumber, newCursorPosition.column)]);
if (primarySelection) {
const { selectionStartLineNumber, selectionStartColumn } = primarySelection;
newSelections = [new Selection(selectionStartLineNumber, selectionStartColumn, newCursorPosition.lineNumber, newCursorPosition.column)];
}
} else {
let newSelections = this._editor.getSelections().map(selection => {
newSelections = (this._editor.getSelections() || []).map(selection => {
if (selection.containsPosition(newCursorPosition)) {
return new Selection(newCursorPosition.lineNumber, newCursorPosition.column, newCursorPosition.lineNumber, newCursorPosition.column);
} else {
return selection;
}
});
this._editor.setSelections(newSelections);
}
// Use `mouse` as the source instead of `api`.
(<CodeEditorWidget>this._editor).setSelections(newSelections || [], 'mouse');
} else if (!this._dragSelection.containsPosition(newCursorPosition) ||
(
(

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Selection } from 'vs/editor/common/core/selection';

View File

@@ -2,20 +2,19 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { DocumentSymbolProviderRegistry, DocumentSymbolProvider, DocumentSymbol } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters';
import { IPosition } from 'vs/editor/common/core/position';
import { Range, IRange } from 'vs/editor/common/core/range';
import { first, size, forEach } from 'vs/base/common/collections';
import { isFalsyOrEmpty, binarySearch, coalesce } from 'vs/base/common/arrays';
import { commonPrefixLength } from 'vs/base/common/strings';
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { LRUCache } from 'vs/base/common/map';
import { binarySearch, coalesceInPlace } from 'vs/base/common/arrays';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { first, forEach, size } from 'vs/base/common/collections';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters';
import { LRUCache } from 'vs/base/common/map';
import { commonPrefixLength } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
export abstract class TreeElement {
@@ -134,7 +133,11 @@ export class OutlineGroup extends TreeElement {
}
private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement): OutlineElement {
item.score = fuzzyScore(pattern, item.symbol.name, undefined, true);
item.score = pattern
? fuzzyScore(pattern, pattern.toLowerCase(), 0, item.symbol.name, item.symbol.name.toLowerCase(), 0, true)
: [-100, []];
if (item.score && (!topMatch || item.score[0] > topMatch.score[0])) {
topMatch = item;
}
@@ -143,20 +146,20 @@ export class OutlineGroup extends TreeElement {
topMatch = this._updateMatches(pattern, child, topMatch);
if (!item.score && child.score) {
// don't filter parents with unfiltered children
item.score = [0, []];
item.score = [-100, []];
}
}
return topMatch;
}
getItemEnclosingPosition(position: IPosition): OutlineElement {
return this._getItemEnclosingPosition(position, this.children);
return position ? this._getItemEnclosingPosition(position, this.children) : undefined;
}
private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement {
for (let key in children) {
let item = children[key];
if (!Range.containsPosition(item.symbol.range, position)) {
if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) {
continue;
}
return this._getItemEnclosingPosition(position, item.children) || item;
@@ -215,7 +218,7 @@ export class OutlineGroup extends TreeElement {
};
}
coalesce(markers, true);
coalesceInPlace(markers);
}
}
@@ -298,10 +301,8 @@ export class OutlineModel extends TreeElement {
let group = new OutlineGroup(id, result, provider, index);
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, token)).then(result => {
if (!isFalsyOrEmpty(result)) {
for (const info of result) {
OutlineModel._makeOutlineElement(info, group);
}
for (const info of result || []) {
OutlineModel._makeOutlineElement(info, group);
}
return group;
}, err => {
@@ -393,11 +394,17 @@ export class OutlineModel extends TreeElement {
return true;
}
private _matches: [string, OutlineElement];
updateMatches(pattern: string): OutlineElement {
if (this._matches && this._matches[0] === pattern) {
return this._matches[1];
}
let topMatch: OutlineElement;
for (const key in this._groups) {
topMatch = this._groups[key].updateMatches(pattern, topMatch);
}
this._matches = [pattern, topMatch];
return topMatch;
}
@@ -414,7 +421,7 @@ export class OutlineModel extends TreeElement {
}
}
let result: OutlineElement = undefined;
let result: OutlineElement | undefined = undefined;
for (const key in this._groups) {
const group = this._groups[key];
result = group.getItemEnclosingPosition(position);

View File

@@ -2,14 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as dom from 'vs/base/browser/dom';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { values } from 'vs/base/common/collections';
import { createMatches } from 'vs/base/common/filters';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
import 'vs/css!./media/outlineTree';
import 'vs/css!./media/symbol-icons';
@@ -97,14 +95,14 @@ export class OutlineDataSource implements IDataSource {
return false;
}
getChildren(tree: ITree, element: TreeElement): TPromise<TreeElement[]> {
getChildren(tree: ITree, element: TreeElement): Promise<TreeElement[]> {
let res = values(element.children);
// console.log(element.id + ' with children ' + res.length);
return TPromise.wrap(res);
return Promise.resolve(res);
}
getParent(tree: ITree, element: TreeElement | any): TPromise<TreeElement> {
return TPromise.wrap(element && element.parent);
getParent(tree: ITree, element: TreeElement | any): Promise<TreeElement> {
return Promise.resolve(element && element.parent);
}
shouldAutoexpand(tree: ITree, element: TreeElement): boolean {
@@ -148,13 +146,13 @@ export class OutlineRenderer implements IRenderer {
const decoration = dom.$('.outline-element-decoration');
dom.addClass(container, 'outline-element');
dom.append(container, icon, labelContainer, detail, decoration);
return { icon, labelContainer, label: new HighlightedLabel(labelContainer), detail, decoration };
return { icon, labelContainer, label: new HighlightedLabel(labelContainer, true), detail, decoration };
}
if (templateId === 'outline-group') {
const labelContainer = dom.$('.outline-element-label');
dom.addClass(container, 'outline-element');
dom.append(container, labelContainer);
return { labelContainer, label: new HighlightedLabel(labelContainer) };
return { labelContainer, label: new HighlightedLabel(labelContainer, true) };
}
throw new Error(templateId);
@@ -285,7 +283,7 @@ export class OutlineTreeState {
static async restore(tree: ITree, state: OutlineTreeState, eventPayload: any): Promise<void> {
let model = <OutlineModel>tree.getInput();
if (!state || !(model instanceof OutlineModel)) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
// expansion

View File

@@ -3,15 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { OutlineElement, OutlineGroup, OutlineModel } from '../outlineModel';
import { SymbolKind, DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { Range } from 'vs/editor/common/core/range';
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { TextModel } from 'vs/editor/common/model/textModel';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
suite('OutlineModel', function () {

View File

@@ -2,30 +2,31 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { Delayer } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import * as strings from 'vs/base/common/strings';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorContribution, registerEditorAction, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, CONTEXT_FIND_WIDGET_VISIBLE } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
import { Delayer } from 'vs/base/common/async';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget';
import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel';
import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IThemeService } from 'vs/platform/theme/common/themeService';
const SEARCH_STRING_MAX_LENGTH = 524288;
export function getSelectionSearchString(editor: ICodeEditor): string {
let selection = editor.getSelection();
@@ -38,7 +39,9 @@ export function getSelectionSearchString(editor: ICodeEditor): string {
return wordAtPosition.word;
}
} else {
return editor.getModel().getValueInRange(selection);
if (editor.getModel().getValueLengthInRange(selection) < SEARCH_STRING_MAX_LENGTH) {
return editor.getModel().getValueInRange(selection);
}
}
}
@@ -57,6 +60,7 @@ export interface IFindStartOptions {
seedSearchStringFromGlobalClipboard: boolean;
shouldFocus: FindStartFocusAction;
shouldAnimate: boolean;
updateSearchScope: boolean;
}
export class CommonFindController extends Disposable implements editorCommon.IEditorContribution {
@@ -68,8 +72,9 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
protected _state: FindReplaceState;
protected _updateHistoryDelayer: Delayer<void>;
private _model: FindModelBoundToEditorModel;
protected _storageService: IStorageService;
private _storageService: IStorageService;
private _clipboardService: IClipboardService;
protected readonly _contextKeyService: IContextKeyService;
public static get(editor: ICodeEditor): CommonFindController {
return editor.getContribution<CommonFindController>(CommonFindController.ID);
@@ -84,6 +89,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
super();
this._editor = editor;
this._findWidgetVisible = CONTEXT_FIND_WIDGET_VISIBLE.bindTo(contextKeyService);
this._contextKeyService = contextKeyService;
this._storageService = storageService;
this._clipboardService = clipboardService;
@@ -113,6 +119,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: false
});
}
}));
@@ -170,6 +177,10 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
}, false);
}
public isFindInputFocused(): boolean {
return CONTEXT_FIND_INPUT_FOCUSED.getValue(this._contextKeyService);
}
public getState(): FindReplaceState {
return this._state;
}
@@ -184,14 +195,23 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
public toggleCaseSensitive(): void {
this._state.change({ matchCase: !this._state.matchCase }, false);
if (!this._state.isRevealed) {
this.highlightFindOptions();
}
}
public toggleWholeWords(): void {
this._state.change({ wholeWord: !this._state.wholeWord }, false);
if (!this._state.isRevealed) {
this.highlightFindOptions();
}
}
public toggleRegex(): void {
this._state.change({ isRegex: !this._state.isRegex }, false);
if (!this._state.isRevealed) {
this.highlightFindOptions();
}
}
public toggleSearchScope(): void {
@@ -200,7 +220,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
} else {
let selection = this._editor.getSelection();
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
selection = selection.setEndPosition(selection.endLineNumber - 1, 1);
selection = selection.setEndPosition(selection.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
}
if (!selection.isEmpty()) {
this._state.change({ searchScope: selection }, true);
@@ -256,6 +276,12 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
stateChanges.isReplaceRevealed = false;
}
if (opts.updateSearchScope) {
let currentSelection = this._editor.getSelection();
if (!currentSelection.isEmpty()) {
stateChanges.searchScope = currentSelection;
}
}
this._state.change(stateChanges, false);
@@ -337,7 +363,7 @@ export class FindController extends CommonFindController implements IFindControl
constructor(
editor: ICodeEditor,
@IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IContextKeyService _contextKeyService: IContextKeyService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService,
@IStorageService storageService: IStorageService,
@@ -351,6 +377,11 @@ export class FindController extends CommonFindController implements IFindControl
this._createFindWidget();
}
if (!this._widget.getPosition() && this._editor.getConfiguration().contribInfo.find.autoFindInSelection) {
// not visible yet so we need to set search scope if `editor.find.autoFindInSelection` is `true`
opts.updateSearchScope = true;
}
super._start(opts);
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
@@ -407,7 +438,8 @@ export class StartFindAction extends EditorAction {
seedSearchStringFromSelection: editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getConfiguration().contribInfo.find.globalFindClipboard,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true
shouldAnimate: true,
updateSearchScope: false
});
}
}
@@ -423,7 +455,7 @@ export class StartFindWithSelectionAction extends EditorAction {
precondition: null,
kbOpts: {
kbExpr: null,
primary: null,
primary: 0,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_E,
},
@@ -440,7 +472,8 @@ export class StartFindWithSelectionAction extends EditorAction {
seedSearchStringFromSelection: true,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true
shouldAnimate: true,
updateSearchScope: false
});
controller.setGlobalBufferTerm(controller.getState().searchString);
@@ -456,7 +489,8 @@ export abstract class MatchFindAction extends EditorAction {
seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: true,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true
shouldAnimate: true,
updateSearchScope: false
});
this._run(controller);
}
@@ -525,7 +559,8 @@ export abstract class SelectionMatchFindAction extends EditorAction {
seedSearchStringFromSelection: editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true
shouldAnimate: true,
updateSearchScope: false
});
this._run(controller);
}
@@ -606,22 +641,31 @@ export class StartFindReplaceAction extends EditorAction {
let controller = CommonFindController.get(editor);
let currentSelection = editor.getSelection();
// we only seed search string from selection when the current selection is single line and not empty.
let seedSearchStringFromSelection = !currentSelection.isEmpty() &&
currentSelection.startLineNumber === currentSelection.endLineNumber && editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection;
let oldSearchString = controller.getState().searchString;
// if the existing search string in find widget is empty and we don't seed search string from selection, it means the Find Input
// is still empty, so we should focus the Find Input instead of Replace Input.
let shouldFocus = (!!oldSearchString || seedSearchStringFromSelection) ?
let findInputFocused = controller.isFindInputFocused();
// we only seed search string from selection when the current selection is single line and not empty,
// + the find input is not focused
let seedSearchStringFromSelection = !currentSelection.isEmpty()
&& currentSelection.startLineNumber === currentSelection.endLineNumber && editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection
&& !findInputFocused;
/*
* if the existing search string in find widget is empty and we don't seed search string from selection, it means the Find Input is still empty, so we should focus the Find Input instead of Replace Input.
* findInputFocused true -> seedSearchStringFromSelection false, FocusReplaceInput
* findInputFocused false, seedSearchStringFromSelection true FocusReplaceInput
* findInputFocused false seedSearchStringFromSelection false FocusFindInput
*/
let shouldFocus = (findInputFocused || seedSearchStringFromSelection) ?
FindStartFocusAction.FocusReplaceInput : FindStartFocusAction.FocusFindInput;
if (controller) {
controller.start({
forceRevealReplace: true,
seedSearchStringFromSelection: seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
shouldFocus: shouldFocus,
shouldAnimate: true
shouldAnimate: true,
updateSearchScope: false
});
}
}

View File

@@ -2,28 +2,27 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { FindMatch, IModelDecorationsChangeAccessor, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IModelDecorationsChangeAccessor, FindMatch, IModelDeltaDecoration, TrackedRangeStickiness, OverviewRulerLane } from 'vs/editor/common/model';
export class FindDecorations implements IDisposable {
private _editor: ICodeEditor;
private _editor: IActiveCodeEditor;
private _decorations: string[];
private _overviewRulerApproximateDecorations: string[];
private _findScopeDecorationId: string;
private _rangeHighlightDecorationId: string;
private _highlightedDecorationId: string;
private _findScopeDecorationId: string | null;
private _rangeHighlightDecorationId: string | null;
private _highlightedDecorationId: string | null;
private _startPosition: Position;
constructor(editor: ICodeEditor) {
constructor(editor: IActiveCodeEditor) {
this._editor = editor;
this._decorations = [];
this._overviewRulerApproximateDecorations = [];
@@ -36,13 +35,11 @@ export class FindDecorations implements IDisposable {
public dispose(): void {
this._editor.deltaDecorations(this._allDecorations(), []);
this._editor = null;
this._decorations = [];
this._overviewRulerApproximateDecorations = [];
this._findScopeDecorationId = null;
this._rangeHighlightDecorationId = null;
this._highlightedDecorationId = null;
this._startPosition = null;
}
public reset(): void {
@@ -57,7 +54,7 @@ export class FindDecorations implements IDisposable {
return this._decorations.length;
}
public getFindScope(): Range {
public getFindScope(): Range | null {
if (this._findScopeDecorationId) {
return this._editor.getModel().getDecorationRange(this._findScopeDecorationId);
}
@@ -93,8 +90,8 @@ export class FindDecorations implements IDisposable {
return 1;
}
public setCurrentFindMatch(nextMatch: Range): number {
let newCurrentDecorationId: string = null;
public setCurrentFindMatch(nextMatch: Range | null): number {
let newCurrentDecorationId: string | null = null;
let matchPosition = 0;
if (nextMatch) {
for (let i = 0, len = this._decorations.length; i < len; i++) {
@@ -122,7 +119,7 @@ export class FindDecorations implements IDisposable {
this._rangeHighlightDecorationId = null;
}
if (newCurrentDecorationId !== null) {
let rng = this._editor.getModel().getDecorationRange(newCurrentDecorationId);
let rng = this._editor.getModel().getDecorationRange(newCurrentDecorationId)!;
if (rng.startLineNumber !== rng.endLineNumber && rng.endColumn === 1) {
let lineBeforeEnd = rng.endLineNumber - 1;
let lineBeforeEndMaxColumn = this._editor.getModel().getLineMaxColumn(lineBeforeEnd);
@@ -136,7 +133,7 @@ export class FindDecorations implements IDisposable {
return matchPosition;
}
public set(findMatches: FindMatch[], findScope: Range): void {
public set(findMatches: FindMatch[], findScope: Range | null): void {
this._editor.changeDecorations((accessor) => {
let findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION;
@@ -208,7 +205,7 @@ export class FindDecorations implements IDisposable {
});
}
public matchBeforePosition(position: Position): Range {
public matchBeforePosition(position: Position): Range | null {
if (this._decorations.length === 0) {
return null;
}
@@ -230,7 +227,7 @@ export class FindDecorations implements IDisposable {
return this._editor.getModel().getDecorationRange(this._decorations[this._decorations.length - 1]);
}
public matchAfterPosition(position: Position): Range {
public matchAfterPosition(position: Position): Range | null {
if (this._decorations.length === 0) {
return null;
}
@@ -272,7 +269,6 @@ export class FindDecorations implements IDisposable {
showIfCollapsed: true,
overviewRuler: {
color: themeColorFromId(overviewRulerFindMatchForeground),
darkColor: themeColorFromId(overviewRulerFindMatchForeground),
position: OverviewRulerLane.Center
}
});
@@ -283,7 +279,6 @@ export class FindDecorations implements IDisposable {
showIfCollapsed: true,
overviewRuler: {
color: themeColorFromId(overviewRulerFindMatchForeground),
darkColor: themeColorFromId(overviewRulerFindMatchForeground),
position: OverviewRulerLane.Center
}
});
@@ -298,7 +293,6 @@ export class FindDecorations implements IDisposable {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
overviewRuler: {
color: themeColorFromId(overviewRulerFindMatchForeground),
darkColor: themeColorFromId(overviewRulerFindMatchForeground),
position: OverviewRulerLane.Center
}
});

View File

@@ -2,27 +2,26 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { FindDecorations } from './findDecorations';
import { FindReplaceState, FindReplaceStateChangedEvent } from './findState';
import { ReplaceAllCommand } from './replaceAllCommand';
import { Selection } from 'vs/editor/common/core/selection';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Constants } from 'vs/editor/common/core/uint';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, FindMatch, ITextModel } from 'vs/editor/common/model';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
import { FindDecorations } from 'vs/editor/contrib/find/findDecorations';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { ReplaceAllCommand } from 'vs/editor/contrib/find/replaceAllCommand';
import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel, FindMatch, EndOfLinePreference } from 'vs/editor/common/model';
export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('findWidgetVisible', false);
export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated();
@@ -70,7 +69,7 @@ const RESEARCH_DELAY = 240;
export class FindModelBoundToEditorModel {
private _editor: ICodeEditor;
private _editor: IActiveCodeEditor;
private _state: FindReplaceState;
private _toDispose: IDisposable[];
private _decorations: FindDecorations;
@@ -80,7 +79,7 @@ export class FindModelBoundToEditorModel {
private _updateDecorationsScheduler: RunOnceScheduler;
private _isDisposed: boolean;
constructor(editor: ICodeEditor, state: FindReplaceState) {
constructor(editor: IActiveCodeEditor, state: FindReplaceState) {
this._editor = editor;
this._state = state;
this._toDispose = [];
@@ -159,19 +158,17 @@ export class FindModelBoundToEditorModel {
}
}
private static _getSearchRange(model: ITextModel, findScope: Range): Range {
let searchRange = model.getFullModelRange();
private static _getSearchRange(model: ITextModel, findScope: Range | null): Range {
// If we have set now or before a find scope, use it for computing the search range
if (findScope) {
searchRange = searchRange.intersectRanges(findScope);
return findScope;
}
return searchRange;
return model.getFullModelRange();
}
private research(moveCursor: boolean, newFindScope?: Range): void {
let findScope: Range = null;
private research(moveCursor: boolean, newFindScope?: Range | null): void {
let findScope: Range | null = null;
if (typeof newFindScope !== 'undefined') {
findScope = newFindScope;
} else {
@@ -179,8 +176,12 @@ export class FindModelBoundToEditorModel {
}
if (findScope !== null) {
if (findScope.startLineNumber !== findScope.endLineNumber) {
// multiline find scope => expand to line starts / ends
findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber));
if (findScope.endColumn === 1) {
findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber - 1, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber - 1));
} else {
// multiline find scope => expand to line starts / ends
findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber));
}
}
}
@@ -296,7 +297,7 @@ export class FindModelBoundToEditorModel {
if (!prevMatch) {
// there is precisely one match and selection is on top of it
return null;
return;
}
if (!isRecursed && !searchRange.containsRange(prevMatch.range)) {
@@ -355,7 +356,7 @@ export class FindModelBoundToEditorModel {
}
}
private _getNextMatch(after: Position, captureMatches: boolean, forceMove: boolean, isRecursed: boolean = false): FindMatch {
private _getNextMatch(after: Position, captureMatches: boolean, forceMove: boolean, isRecursed: boolean = false): FindMatch | null {
if (this._cannotFind()) {
return null;
}
@@ -435,7 +436,7 @@ export class FindModelBoundToEditorModel {
}
}
private _findMatches(findScope: Range, captureMatches: boolean, limitResultCount: number): FindMatch[] {
private _findMatches(findScope: Range | null, captureMatches: boolean, limitResultCount: number): FindMatch[] {
let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), findScope);
return this._editor.getModel().findMatches(this._state.searchString, searchRange, this._state.isRegex, this._state.matchCase, this._state.wholeWord ? this._editor.getConfiguration().wordSeparators : null, captureMatches, limitResultCount);
}
@@ -494,7 +495,7 @@ export class FindModelBoundToEditorModel {
this._executeEditorCommand('replaceAll', command);
}
private _regularReplaceAll(findScope: Range): void {
private _regularReplaceAll(findScope: Range | null): void {
const replacePattern = this._getReplacePattern();
// Get all the ranges (even more than the highlighted ones)
let matches = this._findMatches(findScope, replacePattern.hasReplacementPatterns, Constants.MAX_SAFE_SMALL_INTEGER);

View File

@@ -3,18 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as dom from 'vs/base/browser/dom';
import { CaseSensitiveCheckbox, RegexCheckbox, WholeWordsCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes';
import { Widget } from 'vs/base/browser/ui/widget';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { FIND_IDS } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { CaseSensitiveCheckbox, WholeWordsCheckbox, RegexCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { inputActiveOptionBorder, editorWidgetBackground, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { contrastBorder, editorWidgetBackground, inputActiveOptionBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
export class FindOptionsWidget extends Widget implements IOverlayWidget {
@@ -190,17 +188,17 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
registerThemingParticipant((theme, collector) => {
let widgetBackground = theme.getColor(editorWidgetBackground);
const widgetBackground = theme.getColor(editorWidgetBackground);
if (widgetBackground) {
collector.addRule(`.monaco-editor .findOptionsWidget { background-color: ${widgetBackground}; }`);
}
let widgetShadowColor = theme.getColor(widgetShadow);
const widgetShadowColor = theme.getColor(widgetShadow);
if (widgetShadowColor) {
collector.addRule(`.monaco-editor .findOptionsWidget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
}
let hcBorder = theme.getColor(contrastBorder);
const hcBorder = theme.getColor(contrastBorder);
if (hcBorder) {
collector.addRule(`.monaco-editor .findOptionsWidget { border: 2px solid ${hcBorder}; }`);
}

View File

@@ -2,9 +2,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
@@ -66,10 +65,10 @@ export class FindReplaceState implements IDisposable {
private _wholeWordOverride: FindOptionOverride;
private _matchCase: boolean;
private _matchCaseOverride: FindOptionOverride;
private _searchScope: Range;
private _searchScope: Range | null;
private _matchesPosition: number;
private _matchesCount: number;
private _currentMatch: Range;
private _currentMatch: Range | null;
private readonly _onFindReplaceStateChange: Emitter<FindReplaceStateChangedEvent>;
public get searchString(): string { return this._searchString; }
@@ -84,10 +83,10 @@ export class FindReplaceState implements IDisposable {
public get actualWholeWord(): boolean { return this._wholeWord; }
public get actualMatchCase(): boolean { return this._matchCase; }
public get searchScope(): Range { return this._searchScope; }
public get searchScope(): Range | null { return this._searchScope; }
public get matchesPosition(): number { return this._matchesPosition; }
public get matchesCount(): number { return this._matchesCount; }
public get currentMatch(): Range { return this._currentMatch; }
public get currentMatch(): Range | null { return this._currentMatch; }
public get onFindReplaceStateChange(): Event<FindReplaceStateChangedEvent> { return this._onFindReplaceStateChange.event; }
constructor() {
@@ -111,7 +110,7 @@ export class FindReplaceState implements IDisposable {
public dispose(): void {
}
public changeMatchInfo(matchesPosition: number, matchesCount: number, currentMatch: Range): void {
public changeMatchInfo(matchesPosition: number, matchesCount: number, currentMatch: Range | undefined): void {
let changeEvent: FindReplaceStateChangedEvent = {
moveCursor: false,
updateHistory: false,

View File

@@ -36,13 +36,7 @@
height: 34px; /* find input height */
overflow: hidden;
line-height: 19px;
-webkit-transition: top 200ms linear;
-o-transition: top 200ms linear;
-moz-transition: top 200ms linear;
-ms-transition: top 200ms linear;
transition: top 200ms linear;
padding: 0 4px;
}
/* Find widget when replace is toggled on */
@@ -253,7 +247,7 @@
/* COLLAPSED (SMALLER THAN NARROW) */
.monaco-editor .find-widget.collapsed-find-widget {
max-width: 111px !important;
max-width: 170px !important;
}
.monaco-editor .find-widget.collapsed-find-widget .button.previous,
@@ -264,10 +258,6 @@
display:none;
}
.monaco-editor .find-widget.collapsed-find-widget > .find-part .monaco-inputbox > .wrapper > .input {
padding-right: 0px;
}
.monaco-editor .findMatch {
-webkit-animation-duration: 0;
-webkit-animation-name: inherit !important;

View File

@@ -3,36 +3,34 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./findWidget';
import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { Delayer } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { IMessage as InputBoxMessage, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
import { Sash, IHorizontalSashLayoutProvider, ISashEvent, Orientation } from 'vs/base/browser/ui/sash/sash';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { FIND_IDS, MATCHES_LIMIT, CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { Range } from 'vs/editor/common/core/range';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { Delayer } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { toDisposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, contrastBorder, inputBackground, editorWidgetBackground, inputActiveOptionBorder, widgetShadow, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, errorForeground, editorWidgetBorder, editorFindMatchBorder, editorFindMatchHighlightBorder, editorFindRangeHighlightBorder, editorWidgetResizeBorder } from 'vs/platform/theme/common/colorRegistry';
import { Range } from 'vs/editor/common/core/range';
import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MATCHES_LIMIT } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
export interface IFindController {
replace(): void;
replaceAll(): void;
@@ -106,6 +104,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _isVisible: boolean;
private _isReplaceVisible: boolean;
private _ignoreChangeEvent: boolean;
private _findFocusTracker: dom.IFocusTracker;
private _findInputFocused: IContextKey<boolean>;
@@ -137,8 +136,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._isVisible = false;
this._isReplaceVisible = false;
this._ignoreChangeEvent = false;
this._updateHistoryDelayer = new Delayer<void>(500);
this._register(toDisposable(() => this._updateHistoryDelayer.cancel()));
this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
this._buildDomNode();
this._updateButtons();
@@ -155,7 +156,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (e.layoutInfo) {
this._tryUpdateWidgetWidth();
}
if (e.accessibilitySupport) {
this.updateAccessibilitySupport();
}
}));
this.updateAccessibilitySupport();
this._register(this._codeEditor.onDidChangeCursorSelection(() => {
if (this._isVisible) {
this._updateToggleSelectionFindButton();
@@ -196,15 +202,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._applyTheme(themeService.getTheme());
this._register(themeService.onThemeChange(this._applyTheme.bind(this)));
this._register(this._codeEditor.onDidChangeModel((e) => {
this._register(this._codeEditor.onDidChangeModel(() => {
if (!this._isVisible) {
return;
}
if (this._viewZoneId === undefined) {
return;
}
this._codeEditor.changeViewZones((accessor) => {
accessor.removeZone(this._viewZoneId);
this._viewZoneId = undefined;
@@ -248,7 +252,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
if (e.searchString) {
this._findInput.setValue(this._state.searchString);
try {
this._ignoreChangeEvent = true;
this._findInput.setValue(this._state.searchString);
} finally {
this._ignoreChangeEvent = false;
}
this._updateButtons();
}
if (e.replaceString) {
@@ -256,7 +265,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
if (e.isRevealed) {
if (this._state.isRevealed) {
this._reveal(true);
this._reveal();
} else {
this._hide(true);
}
@@ -297,6 +306,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
dom.toggleClass(this._domNode, 'no-results', showRedOutline);
this._updateMatchesCount();
this._updateButtons();
}
if (e.searchString || e.currentMatch) {
this._layoutViewZone();
@@ -372,8 +382,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._closeBtn.setEnabled(this._isVisible);
let findInputIsNonEmpty = (this._state.searchString.length > 0);
this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty);
this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty);
let matchesCount = this._state.matchesCount ? true : false;
this._prevBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount);
this._nextBtn.setEnabled(this._isVisible && findInputIsNonEmpty && matchesCount);
this._replaceBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty);
this._replaceAllBtn.setEnabled(this._isVisible && this._isReplaceVisible && findInputIsNonEmpty);
@@ -386,7 +397,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._toggleReplaceBtn.setEnabled(this._isVisible && canReplace);
}
private _reveal(animate: boolean): void {
private _reveal(): void {
if (!this._isVisible) {
this._isVisible = true;
@@ -404,6 +415,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
dom.addClass(this._domNode, 'visible');
this._domNode.setAttribute('aria-hidden', 'false');
}, 0);
// validate query again as it's being dismissed when we hide the find widget.
setTimeout(() => {
this._findInput.validate();
}, 200);
this._codeEditor.layoutOverlayWidget(this);
let adjustEditorScrollTop = true;
@@ -441,6 +458,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
dom.removeClass(this._domNode, 'visible');
this._domNode.setAttribute('aria-hidden', 'true');
this._findInput.clearMessage();
if (focusTheEditor) {
this._codeEditor.focus();
}
@@ -512,10 +530,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
inputForeground: theme.getColor(inputForeground),
inputBorder: theme.getColor(inputBorder),
inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground),
inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground),
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder)
};
this._findInput.style(inputStyles);
@@ -591,7 +612,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (this._toggleSelectionFind.checked) {
let selection = this._codeEditor.getSelection();
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
selection = selection.setEndPosition(selection.endLineNumber - 1, 1);
selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
}
let currentMatch = this._state.currentMatch;
if (selection.startLineNumber !== selection.endLineNumber) {
@@ -613,13 +634,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _onFindInputKeyDown(e: IKeyboardEvent): void {
if (e.equals(KeyCode.Enter)) {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError);
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(null, onUnexpectedError);
e.preventDefault();
return;
}
if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError);
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(null, onUnexpectedError);
e.preventDefault();
return;
}
@@ -675,13 +696,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
// ----- sash
public getHorizontalSashTop(sash: Sash): number {
public getHorizontalSashTop(_sash: Sash): number {
return 0;
}
public getHorizontalSashLeft?(sash: Sash): number {
public getHorizontalSashLeft?(_sash: Sash): number {
return 0;
}
public getHorizontalSashWidth?(sash: Sash): number {
public getHorizontalSashWidth?(_sash: Sash): number {
return 500;
}
@@ -720,12 +741,15 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
return { content: e.message };
}
}
}, this._contextKeyService));
}, this._contextKeyService, true));
this._findInput.setRegex(!!this._state.isRegex);
this._findInput.setCaseSensitive(!!this._state.matchCase);
this._findInput.setWholeWords(!!this._state.wholeWord);
this._register(this._findInput.onKeyDown((e) => this._onFindInputKeyDown(e)));
this._register(this._findInput.inputBox.onDidChange(() => {
if (this._ignoreChangeEvent) {
return;
}
this._state.change({ searchString: this._findInput.getValue() }, true);
}));
this._register(this._findInput.onDidOptionChange(() => {
@@ -756,7 +780,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction),
className: 'previous',
onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, onUnexpectedError);
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(null, onUnexpectedError);
}
}));
@@ -765,7 +789,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction),
className: 'next',
onTrigger: () => {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, onUnexpectedError);
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(null, onUnexpectedError);
}
}));
@@ -784,7 +808,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (this._toggleSelectionFind.checked) {
let selection = this._codeEditor.getSelection();
if (selection.endColumn === 1 && selection.endLineNumber > selection.startLineNumber) {
selection = selection.setEndPosition(selection.endLineNumber - 1, 1);
selection = selection.setEndPosition(selection.endLineNumber - 1, this._codeEditor.getModel().getLineMaxColumn(selection.endLineNumber - 1));
}
if (!selection.isEmpty()) {
this._state.change({ searchScope: selection }, true);
@@ -832,8 +856,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
history: []
}, this._contextKeyService));
this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e)));
this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'input', (e) => {
this._register(this._replaceInputBox.onDidChange(() => {
this._state.change({ replaceString: this._replaceInputBox.value }, false);
}));
@@ -912,7 +937,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._resized = false;
let originalWidth = FIND_WIDGET_INITIAL_WIDTH;
this._register(this._resizeSash.onDidStart((e: ISashEvent) => {
this._register(this._resizeSash.onDidStart(() => {
originalWidth = dom.getTotalWidth(this._domNode);
}));
@@ -936,6 +961,11 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}));
}
private updateAccessibilitySupport(): void {
const value = this._codeEditor.getConfiguration().accessibilitySupport;
this._findInput.setFocusInputOnOptionClick(value !== platform.AccessibilitySupport.Enabled);
}
}
interface ISimpleCheckboxOpts {
@@ -979,7 +1009,7 @@ class SimpleCheckbox extends Widget {
this._opts.parent.appendChild(this._domNode);
this.onchange(this._checkbox, (e) => {
this.onchange(this._checkbox, () => {
this._opts.onChange();
});
}
@@ -1142,4 +1172,8 @@ registerThemingParticipant((theme, collector) => {
}
}
const inputActiveBorder = theme.getColor(inputActiveOptionBorder);
if (inputActiveBorder) {
collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { border: 1px solid ${inputActiveBorder.toString()}; }`);
}
});

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';

View File

@@ -5,47 +5,57 @@
import { CharCode } from 'vs/base/common/charCode';
const enum ReplacePatternKind {
StaticValue = 0,
DynamicPieces = 1
}
/**
* Assigned when the replace pattern is entirely static.
*/
class StaticValueReplacePattern {
public readonly kind = ReplacePatternKind.StaticValue;
constructor(public readonly staticValue: string) { }
}
/**
* Assigned when the replace pattern has replacemend patterns.
*/
class DynamicPiecesReplacePattern {
public readonly kind = ReplacePatternKind.DynamicPieces;
constructor(public readonly pieces: ReplacePiece[]) { }
}
export class ReplacePattern {
public static fromStaticValue(value: string): ReplacePattern {
return new ReplacePattern([ReplacePiece.staticValue(value)]);
}
/**
* Assigned when the replace pattern is entirely static.
*/
private readonly _staticValue: string;
private readonly _state: StaticValueReplacePattern | DynamicPiecesReplacePattern;
public get hasReplacementPatterns(): boolean {
return this._staticValue === null;
return (this._state.kind === ReplacePatternKind.DynamicPieces);
}
/**
* Assigned when the replace pattern has replacemend patterns.
*/
private readonly _pieces: ReplacePiece[];
constructor(pieces: ReplacePiece[]) {
constructor(pieces: ReplacePiece[] | null) {
if (!pieces || pieces.length === 0) {
this._staticValue = '';
this._pieces = null;
this._state = new StaticValueReplacePattern('');
} else if (pieces.length === 1 && pieces[0].staticValue !== null) {
this._staticValue = pieces[0].staticValue;
this._pieces = null;
this._state = new StaticValueReplacePattern(pieces[0].staticValue);
} else {
this._staticValue = null;
this._pieces = pieces;
this._state = new DynamicPiecesReplacePattern(pieces);
}
}
public buildReplaceString(matches: string[]): string {
if (this._staticValue !== null) {
return this._staticValue;
public buildReplaceString(matches: string[] | null): string {
if (this._state.kind === ReplacePatternKind.StaticValue) {
return this._state.staticValue;
}
let result = '';
for (let i = 0, len = this._pieces.length; i < len; i++) {
let piece = this._pieces[i];
for (let i = 0, len = this._state.pieces.length; i < len; i++) {
let piece = this._state.pieces[i];
if (piece.staticValue !== null) {
// static value ReplacePiece
result += piece.staticValue;
@@ -59,7 +69,10 @@ export class ReplacePattern {
return result;
}
private static _substitute(matchIndex: number, matches: string[]): string {
private static _substitute(matchIndex: number, matches: string[] | null): string {
if (matches === null) {
return '';
}
if (matchIndex === 0) {
return matches[0];
}
@@ -91,10 +104,10 @@ export class ReplacePiece {
return new ReplacePiece(null, index);
}
public readonly staticValue: string;
public readonly staticValue: string | null;
public readonly matchIndex: number;
private constructor(staticValue: string, matchIndex: number) {
private constructor(staticValue: string | null, matchIndex: number) {
this.staticValue = staticValue;
this.matchIndex = matchIndex;
}
@@ -185,7 +198,7 @@ export function parseReplaceString(replaceString: string): ReplacePattern {
}
let nextChCode = replaceString.charCodeAt(i);
// let replaceWithCharacter: string = null;
// let replaceWithCharacter: string | null = null;
switch (nextChCode) {
case CharCode.Backslash:

View File

@@ -8,10 +8,11 @@
z-index: 10;
position: absolute;
top: 0;
right: 28px;
right: 18px;
width: 220px;
max-width: calc(100% - 28px - 28px - 8px);
pointer-events: none;
padding: 0 10px 10px;
}
.monaco-workbench .simple-find-part {
@@ -22,11 +23,6 @@
padding: 4px;
align-items: center;
pointer-events: all;
-webkit-transition: top 200ms linear;
-o-transition: top 200ms linear;
-moz-transition: top 200ms linear;
-ms-transition: top 200ms linear;
transition: top 200ms linear;
}
@@ -38,10 +34,6 @@
flex: 1;
}
/* Temporarily we don't show match numbers */
.monaco-workbench .simple-find-part .monaco-findInput .controls {
display: none;
}
.monaco-workbench .simple-find-part .monaco-findInput .monaco-inputbox .wrapper .input {
width: 100% !important;
}

View File

@@ -5,17 +5,18 @@
import 'vs/css!./simpleFindWidget';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
import { Widget } from 'vs/base/browser/ui/widget';
import { Delayer } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as dom from 'vs/base/browser/dom';
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { SimpleButton } from './findWidget';
import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { SimpleButton } from 'vs/editor/contrib/find/findWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { editorWidgetBackground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
@@ -27,21 +28,23 @@ export abstract class SimpleFindWidget extends Widget {
private _findInput: FindInput;
private _domNode: HTMLElement;
private _innerDomNode: HTMLElement;
private _isVisible: boolean;
private _isVisible: boolean = false;
private _focusTracker: dom.IFocusTracker;
private _findInputFocusTracker: dom.IFocusTracker;
private _updateHistoryDelayer: Delayer<void>;
constructor(
@IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService
@IContextKeyService contextKeyService: IContextKeyService,
private readonly _state: FindReplaceState = new FindReplaceState(),
showOptionButtons?: boolean
) {
super();
this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewService, {
label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
}, contextKeyService));
}, contextKeyService, showOptionButtons));
// Find History with update delayer
this._updateHistoryDelayer = new Delayer<void>(500);
@@ -51,6 +54,24 @@ export abstract class SimpleFindWidget extends Widget {
this._delayedUpdateHistory();
});
this._findInput.setRegex(!!this._state.isRegex);
this._findInput.setCaseSensitive(!!this._state.matchCase);
this._findInput.setWholeWords(!!this._state.wholeWord);
this._register(this._findInput.onDidOptionChange(() => {
this._state.change({
isRegex: this._findInput.getRegex(),
wholeWord: this._findInput.getWholeWords(),
matchCase: this._findInput.getCaseSensitive()
}, true);
}));
this._register(this._state.onFindReplaceStateChange(() => {
this._findInput.setRegex(this._state.isRegex);
this._findInput.setWholeWords(this._state.wholeWord);
this._findInput.setCaseSensitive(this._state.matchCase);
}));
this._register(this._findInput.onKeyDown((e) => {
if (e.equals(KeyCode.Enter)) {
this.find(false);
@@ -134,7 +155,7 @@ export abstract class SimpleFindWidget extends Widget {
}
public get focusTracker(): dom.IFocusTracker {
return this._findInputFocusTracker;
return this._focusTracker;
}
public updateTheme(theme: ITheme): void {
@@ -144,10 +165,13 @@ export abstract class SimpleFindWidget extends Widget {
inputForeground: theme.getColor(inputForeground),
inputBorder: theme.getColor(inputBorder),
inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground),
inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground),
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder)
};
this._findInput.style(inputStyles);
@@ -187,6 +211,19 @@ export abstract class SimpleFindWidget extends Widget {
}, 0);
}
public show(initialInput?: string): void {
if (initialInput && !this._isVisible) {
this._findInput.setValue(initialInput);
}
this._isVisible = true;
setTimeout(() => {
dom.addClass(this._innerDomNode, 'visible');
this._innerDomNode.setAttribute('aria-hidden', 'false');
}, 0);
}
public hide(): void {
if (this._isVisible) {
this._isVisible = false;
@@ -203,6 +240,18 @@ export abstract class SimpleFindWidget extends Widget {
protected _updateHistory() {
this._findInput.inputBox.addToHistory();
}
protected _getRegexValue(): boolean {
return this._findInput.getRegex();
}
protected _getWholeWordValue(): boolean {
return this._findInput.getWholeWords();
}
protected _getCaseSensitiveValue(): boolean {
return this._findInput.getCaseSensitive();
}
}
// theming

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Position } from 'vs/editor/common/core/position';

View File

@@ -2,29 +2,31 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { TPromise } from 'vs/base/common/winjs.base';
import { Delayer } from 'vs/base/common/async';
import { Event } from 'vs/base/common/event';
import * as platform from 'vs/base/common/platform';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { Range } from 'vs/editor/common/core/range';
import * as platform from 'vs/base/common/platform';
import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, StartFindAction, NextSelectionMatchFindAction } from 'vs/editor/contrib/find/findController';
import { Selection } from 'vs/editor/common/core/selection';
import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController';
import { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { Delayer } from 'vs/base/common/async';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IStorageService } from 'vs/platform/storage/common/storage';
export class TestFindController extends CommonFindController {
public hasFocus: boolean;
public delayUpdateHistory: boolean = false;
public delayedUpdateHistoryPromise: TPromise<void>;
public delayedUpdateHistoryPromise: Promise<void>;
private _findInputFocused: IContextKey<boolean>;
constructor(
editor: ICodeEditor,
@@ -33,6 +35,7 @@ export class TestFindController extends CommonFindController {
@IClipboardService clipboardService: IClipboardService
) {
super(editor, contextKeyService, storageService, clipboardService);
this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService);
this._updateHistoryDelayer = new Delayer<void>(50);
}
@@ -42,6 +45,9 @@ export class TestFindController extends CommonFindController {
if (opts.shouldFocus !== FindStartFocusAction.NoFocusChange) {
this.hasFocus = true;
}
let inputFocused = opts.shouldFocus === FindStartFocusAction.FocusFindInput;
this._findInputFocused.set(inputFocused);
}
}
@@ -54,9 +60,14 @@ suite('FindController', () => {
let clipboardState = '';
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, {
_serviceBrand: undefined,
onDidChangeStorage: Event.None,
onWillSaveState: Event.None,
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
getInteger: (key: string) => undefined,
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
remove: (key) => void 0
} as IStorageService);
if (platform.isMacintosh) {
@@ -248,6 +259,34 @@ suite('FindController', () => {
});
});
test('issue #41027: Don\'t replace find input value on replace action if find input is active', () => {
withTestCodeEditor([
'test',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let testRegexString = 'tes.';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let nextMatchFindAction = new NextMatchFindAction();
let startFindReplaceAction = new StartFindReplaceAction();
findController.toggleRegex();
findController.setSearchString(testRegexString);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: false,
updateSearchScope: false
});
nextMatchFindAction.run(null, editor);
startFindReplaceAction.run(null, editor);
assert.equal(findController.getState().searchString, testRegexString);
findController.dispose();
});
});
test('issue #9043: Clear search scope when find widget is hidden', () => {
withTestCodeEditor([
'var x = (3 * 5)',
@@ -261,7 +300,8 @@ suite('FindController', () => {
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false
shouldAnimate: false,
updateSearchScope: false
});
assert.equal(findController.getState().searchScope, null);
@@ -395,9 +435,14 @@ suite('FindController query options persistence', () => {
queryState['editor.wholeWord'] = false;
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, {
_serviceBrand: undefined,
onDidChangeStorage: Event.None,
onWillSaveState: Event.None,
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
getInteger: (key: string) => undefined,
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
remove: (key) => void 0
} as IStorageService);
test('matchCase', () => {
@@ -468,4 +513,73 @@ suite('FindController query options persistence', () => {
findController.dispose();
});
});
test('issue #27083: Update search scope once find widget becomes visible', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: true, globalFindClipboard: false } }, (editor, cursor) => {
// clipboardState = '';
editor.setSelection(new Range(1, 1, 2, 1));
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
});
assert.deepEqual(findController.getState().searchScope, new Selection(1, 1, 2, 1));
});
});
test('issue #58604: Do not update searchScope if it is empty', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: true, globalFindClipboard: false } }, (editor, cursor) => {
// clipboardState = '';
editor.setSelection(new Range(1, 2, 1, 2));
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
});
assert.deepEqual(findController.getState().searchScope, null);
});
});
test('issue #58604: Update searchScope if it is not empty', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection, find: { autoFindInSelection: true, globalFindClipboard: false } }, (editor, cursor) => {
// clipboardState = '';
editor.setSelection(new Range(1, 2, 1, 3));
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
updateSearchScope: true
});
assert.deepEqual(findController.getState().searchScope, new Selection(1, 2, 1, 3));
});
});
});

View File

@@ -2,20 +2,19 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
import { TextModel } from 'vs/editor/common/model/textModel';
import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { TextModel } from 'vs/editor/common/model/textModel';
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
suite('FindModel', () => {
@@ -2031,4 +2030,22 @@ suite('FindModel', () => {
findModel.dispose();
findState.dispose();
});
findTest('issue #27083. search scope works even if it is a single line', (editor, cursor) => {
let findState = new FindReplaceState();
findState.change({ searchString: 'hello', wholeWord: true, searchScope: new Range(7, 1, 8, 1) }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assertFindState(
editor,
[1, 1, 1, 1],
null,
[
[7, 14, 7, 19]
]
);
findModel.dispose();
findState.dispose();
});
});

View File

@@ -2,10 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { parseReplaceString, ReplacePattern, ReplacePiece } from 'vs/editor/contrib/find/replacePattern';
import { ReplacePattern, ReplacePiece, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
suite('Replace Pattern test', () => {

View File

@@ -4,12 +4,11 @@
*--------------------------------------------------------------------------------------------*/
.monaco-editor .margin-view-overlays .folding {
margin-left: 5px;
cursor: pointer;
background-repeat: no-repeat;
background-origin: border-box;
background-position: 3px center;
background-size: 15px;
background-position: calc(50% + 2px) center;
background-size: auto calc(100% - 3px);
opacity: 0;
transition: opacity 0.5s;
}
@@ -45,5 +44,4 @@
display: inline;
line-height: 1em;
cursor: pointer;
}
}

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./folding';
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
@@ -12,14 +10,13 @@ import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { RunOnceScheduler, Delayer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType } from 'vs/editor/contrib/folding/foldingModel';
import { FoldingDecorationProvider } from './foldingDecorations';
import { FoldingRegions } from './foldingRanges';
import { FoldingRegions, FoldingRegion } from './foldingRanges';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
@@ -39,7 +36,7 @@ export const ID = 'editor.contrib.folding';
export interface RangeProvider {
readonly id: string;
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions>;
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions | null>;
dispose(): void;
}
@@ -65,20 +62,20 @@ export class FoldingController implements IEditorContribution {
private foldingDecorationProvider: FoldingDecorationProvider;
private foldingModel: FoldingModel;
private hiddenRangeModel: HiddenRangeModel;
private foldingModel: FoldingModel | null;
private hiddenRangeModel: HiddenRangeModel | null;
private rangeProvider: RangeProvider;
private foldingRegionPromise: CancelablePromise<FoldingRegions>;
private rangeProvider: RangeProvider | null;
private foldingRegionPromise: CancelablePromise<FoldingRegions | null> | null;
private foldingStateMemento: FoldingStateMemento;
private foldingStateMemento: FoldingStateMemento | null;
private foldingModelPromise: TPromise<FoldingModel>;
private updateScheduler: Delayer<FoldingModel>;
private foldingModelPromise: Thenable<FoldingModel | null> | null;
private updateScheduler: Delayer<FoldingModel | null> | null;
private globalToDispose: IDisposable[];
private cursorChangedScheduler: RunOnceScheduler;
private cursorChangedScheduler: RunOnceScheduler | null;
private localToDispose: IDisposable[];
@@ -132,13 +129,13 @@ export class FoldingController implements IEditorContribution {
/**
* Store view state.
*/
public saveViewState(): FoldingStateMemento {
public saveViewState(): FoldingStateMemento | undefined {
let model = this.editor.getModel();
if (!model || !this._isEnabled || model.isTooLargeForTokenization()) {
return {};
}
if (this.foldingModel) { // disposed ?
let collapsedRegions = this.foldingModel.isInitialized ? this.foldingModel.getMemento() : this.hiddenRangeModel.getMemento();
let collapsedRegions = this.foldingModel.isInitialized ? this.foldingModel.getMemento() : this.hiddenRangeModel!.getMemento();
let provider = this.rangeProvider ? this.rangeProvider.id : void 0;
return { collapsedRegions, lineCount: model.getLineCount(), provider };
}
@@ -150,7 +147,7 @@ export class FoldingController implements IEditorContribution {
*/
public restoreViewState(state: FoldingStateMemento): void {
let model = this.editor.getModel();
if (!model || !this._isEnabled || model.isTooLargeForTokenization()) {
if (!model || !this._isEnabled || model.isTooLargeForTokenization() || !this.hiddenRangeModel) {
return;
}
if (!state || !state.collapsedRegions || state.lineCount !== model.getLineCount()) {
@@ -161,13 +158,18 @@ export class FoldingController implements IEditorContribution {
this.foldingStateMemento = state;
}
const collapsedRegions = state.collapsedRegions;
// set the hidden ranges right away, before waiting for the folding model.
if (this.hiddenRangeModel.applyMemento(state.collapsedRegions)) {
this.getFoldingModel().then(foldingModel => {
if (foldingModel) {
foldingModel.applyMemento(state.collapsedRegions);
}
}).done(undefined, onUnexpectedError);
if (this.hiddenRangeModel.applyMemento(collapsedRegions)) {
const foldingModel = this.getFoldingModel();
if (foldingModel) {
foldingModel.then(foldingModel => {
if (foldingModel) {
foldingModel.applyMemento(collapsedRegions);
}
}).then(undefined, onUnexpectedError);
}
}
}
@@ -191,9 +193,9 @@ export class FoldingController implements IEditorContribution {
this.cursorChangedScheduler = new RunOnceScheduler(() => this.revealCursor(), 200);
this.localToDispose.push(this.cursorChangedScheduler);
this.localToDispose.push(this.editor.onDidChangeModelLanguageConfiguration(e => this.onModelContentChanged())); // covers model language changes as well
this.localToDispose.push(this.editor.onDidChangeModelContent(e => this.onModelContentChanged()));
this.localToDispose.push(this.editor.onDidChangeCursorPosition(e => this.onCursorPositionChanged()));
this.localToDispose.push(this.editor.onDidChangeModelLanguageConfiguration(() => this.onModelContentChanged())); // covers model language changes as well
this.localToDispose.push(this.editor.onDidChangeModelContent(() => this.onModelContentChanged()));
this.localToDispose.push(this.editor.onDidChangeCursorPosition(() => this.onCursorPositionChanged()));
this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
this.localToDispose.push({
@@ -202,7 +204,9 @@ export class FoldingController implements IEditorContribution {
this.foldingRegionPromise.cancel();
this.foldingRegionPromise = null;
}
this.updateScheduler.cancel();
if (this.updateScheduler) {
this.updateScheduler.cancel();
}
this.updateScheduler = null;
this.foldingModel = null;
this.foldingModelPromise = null;
@@ -232,15 +236,16 @@ export class FoldingController implements IEditorContribution {
}
this.rangeProvider = new IndentRangeProvider(editorModel); // fallback
if (this._useFoldingProviders) {
if (this._useFoldingProviders && this.foldingModel) {
let foldingProviders = FoldingRangeProviderRegistry.ordered(this.foldingModel.textModel);
if (foldingProviders.length === 0 && this.foldingStateMemento) {
this.rangeProvider = new InitializingRangeProvider(editorModel, this.foldingStateMemento.collapsedRegions, () => {
if (foldingProviders.length === 0 && this.foldingStateMemento && this.foldingStateMemento.collapsedRegions) {
const rangeProvider = this.rangeProvider = new InitializingRangeProvider(editorModel, this.foldingStateMemento.collapsedRegions, () => {
// if after 30 the InitializingRangeProvider is still not replaced, force a refresh
this.foldingStateMemento = null;
this.onFoldingStrategyChanged();
}, 30000);
return this.rangeProvider; // keep memento in case there are still no foldingProviders on the next request.
return rangeProvider; // keep memento in case there are still no foldingProviders on the next request.
} else if (foldingProviders.length > 0) {
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders);
}
@@ -260,25 +265,26 @@ export class FoldingController implements IEditorContribution {
this.foldingRegionPromise = null;
}
this.foldingModelPromise = this.updateScheduler.trigger(() => {
if (!this.foldingModel) { // null if editor has been disposed, or folding turned off
const foldingModel = this.foldingModel;
if (!foldingModel) { // null if editor has been disposed, or folding turned off
return null;
}
let foldingRegionPromise = this.foldingRegionPromise = createCancelablePromise(token => this.getRangeProvider(this.foldingModel.textModel).compute(token));
return TPromise.wrap(foldingRegionPromise.then(foldingRanges => {
let foldingRegionPromise = this.foldingRegionPromise = createCancelablePromise(token => this.getRangeProvider(foldingModel.textModel).compute(token));
return foldingRegionPromise.then(foldingRanges => {
if (foldingRanges && foldingRegionPromise === this.foldingRegionPromise) { // new request or cancelled in the meantime?
// some cursors might have moved into hidden regions, make sure they are in expanded regions
let selections = this.editor.getSelections();
let selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : [];
this.foldingModel.update(foldingRanges, selectionLineNumbers);
foldingModel.update(foldingRanges, selectionLineNumbers);
}
return this.foldingModel;
}));
return foldingModel;
});
});
}
}
private onHiddenRangesChanges(hiddenRanges: IRange[]) {
if (hiddenRanges.length) {
if (this.hiddenRangeModel && hiddenRanges.length) {
let selections = this.editor.getSelections();
if (selections) {
if (this.hiddenRangeModel.adjustSelections(selections)) {
@@ -290,20 +296,24 @@ export class FoldingController implements IEditorContribution {
}
private onCursorPositionChanged() {
if (this.hiddenRangeModel.hasRanges()) {
this.cursorChangedScheduler.schedule();
if (this.hiddenRangeModel && this.hiddenRangeModel.hasRanges()) {
this.cursorChangedScheduler!.schedule();
}
}
private revealCursor() {
this.getFoldingModel().then(foldingModel => { // null is returned if folding got disabled in the meantime
const foldingModel = this.getFoldingModel();
if (!foldingModel) {
return;
}
foldingModel.then(foldingModel => { // null is returned if folding got disabled in the meantime
if (foldingModel) {
let selections = this.editor.getSelections();
if (selections && selections.length > 0) {
let toToggle = [];
let toToggle: FoldingRegion[] = [];
for (let selection of selections) {
let lineNumber = selection.selectionStartLineNumber;
if (this.hiddenRangeModel.isHidden(lineNumber)) {
if (this.hiddenRangeModel && this.hiddenRangeModel.isHidden(lineNumber)) {
toToggle.push(...foldingModel.getAllRegionsAtLine(lineNumber, r => r.isCollapsed && lineNumber > r.startLineNumber));
}
}
@@ -313,30 +323,34 @@ export class FoldingController implements IEditorContribution {
}
}
}
}).done(undefined, onUnexpectedError);
}).then(undefined, onUnexpectedError);
}
private mouseDownInfo: { lineNumber: number, iconClicked: boolean };
private mouseDownInfo: { lineNumber: number, iconClicked: boolean } | null;
private onEditorMouseDown(e: IEditorMouseEvent): void {
this.mouseDownInfo = null;
let range = e.target.range;
if (!this.hiddenRangeModel || !range) {
if (!this.hiddenRangeModel || !e.target || !e.target.range) {
return;
}
if (!e.event.leftButton && !e.event.middleButton) {
return;
}
const range = e.target.range;
let iconClicked = false;
switch (e.target.type) {
case MouseTargetType.GUTTER_LINE_DECORATIONS:
const data = e.target.detail as IMarginData;
const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft;
const offsetLeftInGutter = (e.target.element as HTMLElement).offsetLeft;
const gutterOffsetX = data.offsetX - offsetLeftInGutter;
// const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft;
// TODO@joao TODO@alex TODO@martin this is such that we don't collide with dirty diff
if (gutterOffsetX <= 10) {
if (gutterOffsetX < 5) { // the whitespace between the border and the real folding icon border is 5px
return;
}
@@ -368,7 +382,8 @@ export class FoldingController implements IEditorContribution {
}
private onEditorMouseUp(e: IEditorMouseEvent): void {
if (!this.mouseDownInfo) {
const foldingModel = this.getFoldingModel();
if (!foldingModel || !this.mouseDownInfo || !e.target) {
return;
}
let lineNumber = this.mouseDownInfo.lineNumber;
@@ -385,12 +400,12 @@ export class FoldingController implements IEditorContribution {
}
} else {
let model = this.editor.getModel();
if (range.startColumn !== model.getLineMaxColumn(lineNumber)) {
if (!model || range.startColumn !== model.getLineMaxColumn(lineNumber)) {
return;
}
}
this.getFoldingModel().then(foldingModel => {
foldingModel.then(foldingModel => {
if (foldingModel) {
let region = foldingModel.getRegionAtLine(lineNumber);
if (region && region.startLineNumber === lineNumber) {
@@ -405,7 +420,7 @@ export class FoldingController implements IEditorContribution {
}
}
}
}).done(undefined, onUnexpectedError);
}).then(undefined, onUnexpectedError);
}
public reveal(position: IPosition): void {
@@ -417,7 +432,7 @@ abstract class FoldingAction<T> extends EditorAction {
abstract invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: T): void;
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: T): void | TPromise<void> {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: T): void | Thenable<void> {
let foldingController = FoldingController.get(editor);
if (!foldingController) {
return;
@@ -428,7 +443,10 @@ abstract class FoldingAction<T> extends EditorAction {
return foldingModelPromise.then(foldingModel => {
if (foldingModel) {
this.invoke(foldingController, foldingModel, editor, args);
foldingController.reveal(editor.getSelection().getStartPosition());
const selection = editor.getSelection();
if (selection) {
foldingController.reveal(selection.getStartPosition());
}
}
});
}
@@ -446,7 +464,7 @@ abstract class FoldingAction<T> extends EditorAction {
return this.getSelectedLines(editor);
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
public run(_accessor: ServicesAccessor, _editor: ICodeEditor): void {
}
}
@@ -508,7 +526,7 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: FoldingArguments): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: FoldingArguments): void {
let levels = args && args.levels || 1;
let lineNumbers = this.getLineNumbers(args, editor);
if (args && args.direction === 'up') {
@@ -535,7 +553,7 @@ class UnFoldRecursivelyAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: any): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, _args: any): void {
setCollapseStateLevelsDown(foldingModel, false, Number.MAX_VALUE, this.getSelectedLines(editor));
}
}
@@ -573,7 +591,7 @@ class FoldAction extends FoldingAction<FoldingArguments> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: FoldingArguments): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: FoldingArguments): void {
let levels = args && args.levels || 1;
let lineNumbers = this.getLineNumbers(args, editor);
if (args && args.direction === 'up') {
@@ -600,7 +618,7 @@ class FoldRecursivelyAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
let selectedLines = this.getSelectedLines(editor);
setCollapseStateLevelsDown(foldingModel, true, Number.MAX_VALUE, selectedLines);
}
@@ -622,11 +640,15 @@ class FoldAllBlockCommentsAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
if (foldingModel.regions.hasTypes()) {
setCollapseStateForType(foldingModel, FoldingRangeKind.Comment.value, true);
} else {
let comments = LanguageConfigurationRegistry.getComments(editor.getModel().getLanguageIdentifier().id);
const editorModel = editor.getModel();
if (!editorModel) {
return;
}
let comments = LanguageConfigurationRegistry.getComments(editorModel.getLanguageIdentifier().id);
if (comments && comments.blockCommentStartToken) {
let regExp = new RegExp('^\\s*' + escapeRegExpCharacters(comments.blockCommentStartToken));
setCollapseStateForMatchingLines(foldingModel, regExp, true);
@@ -651,11 +673,15 @@ class FoldAllRegionsAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
if (foldingModel.regions.hasTypes()) {
setCollapseStateForType(foldingModel, FoldingRangeKind.Region.value, true);
} else {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editor.getModel().getLanguageIdentifier().id);
const editorModel = editor.getModel();
if (!editorModel) {
return;
}
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
if (foldingRules && foldingRules.markers && foldingRules.markers.start) {
let regExp = new RegExp(foldingRules.markers.start);
setCollapseStateForMatchingLines(foldingModel, regExp, true);
@@ -680,11 +706,15 @@ class UnfoldAllRegionsAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
if (foldingModel.regions.hasTypes()) {
setCollapseStateForType(foldingModel, FoldingRangeKind.Region.value, false);
} else {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editor.getModel().getLanguageIdentifier().id);
const editorModel = editor.getModel();
if (!editorModel) {
return;
}
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
if (foldingRules && foldingRules.markers && foldingRules.markers.start) {
let regExp = new RegExp(foldingRules.markers.start);
setCollapseStateForMatchingLines(foldingModel, regExp, false);
@@ -709,7 +739,7 @@ class FoldAllAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, _editor: ICodeEditor): void {
setCollapseStateLevelsDown(foldingModel, true);
}
}
@@ -730,7 +760,7 @@ class UnfoldAllAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, _editor: ICodeEditor): void {
setCollapseStateLevelsDown(foldingModel, false);
}
}
@@ -743,7 +773,7 @@ class FoldLevelAction extends FoldingAction<void> {
return parseInt(this.id.substr(FoldLevelAction.ID_PREFIX.length));
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
setCollapseStateAtLevel(foldingModel, this.getFoldingLevel(), true, this.getSelectedLines(editor));
}
}

View File

@@ -10,7 +10,7 @@ import { FoldingRegions, ILineRange, FoldingRegion } from './foldingRanges';
export interface IDecorationProvider {
getDecorationOption(isCollapsed: boolean): IModelDecorationOptions;
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[];
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T;
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null;
}
export interface FoldingModelChangeEvent {
@@ -64,7 +64,7 @@ export class FoldingModel {
}
public update(newRegions: FoldingRegions, blockedLineNumers: number[] = []): void {
let newEditorDecorations = [];
let newEditorDecorations: IModelDeltaDecoration[] = [];
let isBlocked = (startLineNumber, endLineNumber) => {
for (let blockedLineNumber of blockedLineNumers) {
@@ -138,7 +138,7 @@ export class FoldingModel {
/**
* Collapse state memento, for persistence only
*/
public getMemento(): CollapseMemento {
public getMemento(): CollapseMemento | undefined {
let collapsedRanges: ILineRange[] = [];
for (let i = 0; i < this._regions.length; i++) {
if (this._regions.isCollapsed(i)) {
@@ -153,7 +153,7 @@ export class FoldingModel {
if (collapsedRanges.length > 0) {
return collapsedRanges;
}
return null;
return void 0;
}
/**
@@ -194,7 +194,7 @@ export class FoldingModel {
return result;
}
getRegionAtLine(lineNumber: number): FoldingRegion {
getRegionAtLine(lineNumber: number): FoldingRegion | null {
if (this._regions) {
let index = this._regions.findRange(lineNumber);
if (index >= 0) {
@@ -204,16 +204,16 @@ export class FoldingModel {
return null;
}
getRegionsInside(region: FoldingRegion, filter?: (r: FoldingRegion, level?: number) => boolean): FoldingRegion[] {
let result = [];
let trackLevel = filter && filter.length === 2;
let levelStack: FoldingRegion[] = trackLevel ? [] : null;
getRegionsInside(region: FoldingRegion | null, filter?: (r: FoldingRegion, level?: number) => boolean): FoldingRegion[] {
let result: FoldingRegion[] = [];
let index = region ? region.regionIndex + 1 : 0;
let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE;
for (let i = index, len = this._regions.length; i < len; i++) {
let current = this._regions.toRegion(i);
if (this._regions.getStartLineNumber(i) < endLineNumber) {
if (trackLevel) {
if (filter && filter.length === 2) {
const levelStack: FoldingRegion[] = [];
for (let i = index, len = this._regions.length; i < len; i++) {
let current = this._regions.toRegion(i);
if (this._regions.getStartLineNumber(i) < endLineNumber) {
while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) {
levelStack.pop();
}
@@ -221,11 +221,20 @@ export class FoldingModel {
if (filter(current, levelStack.length)) {
result.push(current);
}
} else if (!filter || filter(current)) {
result.push(current);
} else {
break;
}
}
} else {
for (let i = index, len = this._regions.length; i < len; i++) {
let current = this._regions.toRegion(i);
if (this._regions.getStartLineNumber(i) < endLineNumber) {
if (!filter || filter(current)) {
result.push(current);
}
} else {
break;
}
} else {
break;
}
}
return result;
@@ -242,7 +251,7 @@ export class FoldingModel {
* @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model.
*/
export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollapse: boolean, levels = Number.MAX_VALUE, lineNumbers?: number[]) {
let toToggle = [];
let toToggle: FoldingRegion[] = [];
if (lineNumbers && lineNumbers.length > 0) {
for (let lineNumber of lineNumbers) {
let region = foldingModel.getRegionAtLine(lineNumber);
@@ -251,13 +260,13 @@ export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollaps
toToggle.push(region);
}
if (levels > 1) {
let regionsInside = foldingModel.getRegionsInside(region, (r, level) => r.isCollapsed !== doCollapse && level < levels);
let regionsInside = foldingModel.getRegionsInside(region, (r, level: number) => r.isCollapsed !== doCollapse && level < levels);
toToggle.push(...regionsInside);
}
}
}
} else {
let regionsInside = foldingModel.getRegionsInside(null, (r, level) => r.isCollapsed !== doCollapse && level < levels);
let regionsInside = foldingModel.getRegionsInside(null, (r, level: number) => r.isCollapsed !== doCollapse && level < levels);
toToggle.push(...regionsInside);
}
foldingModel.toggleCollapseState(toToggle);
@@ -270,7 +279,7 @@ export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollaps
* @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model.
*/
export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse: boolean, levels: number, lineNumbers: number[]) {
let toToggle = [];
let toToggle: FoldingRegion[] = [];
for (let lineNumber of lineNumbers) {
let regions = foldingModel.getAllRegionsAtLine(lineNumber, (region, level) => region.isCollapsed !== doCollapse && level <= levels);
toToggle.push(...regions);
@@ -297,7 +306,7 @@ export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: n
export function setCollapseStateForMatchingLines(foldingModel: FoldingModel, regExp: RegExp, doCollapse: boolean): void {
let editorModel = foldingModel.textModel;
let regions = foldingModel.regions;
let toToggle = [];
let toToggle: FoldingRegion[] = [];
for (let i = regions.length - 1; i >= 0; i--) {
if (doCollapse !== regions.isCollapsed(i)) {
let startLineNumber = regions.getStartLineNumber(i);
@@ -315,7 +324,7 @@ export function setCollapseStateForMatchingLines(foldingModel: FoldingModel, reg
*/
export function setCollapseStateForType(foldingModel: FoldingModel, type: string, doCollapse: boolean): void {
let regions = foldingModel.regions;
let toToggle = [];
let toToggle: FoldingRegion[] = [];
for (let i = regions.length - 1; i >= 0; i--) {
if (doCollapse !== regions.isCollapsed(i) && type === regions.getType(i)) {
toToggle.push(regions.toRegion(i));

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export interface ILineRange {
startLineNumber: number;
endLineNumber: number;
@@ -20,9 +18,9 @@ export class FoldingRegions {
private _endIndexes: Uint32Array;
private _collapseStates: Uint32Array;
private _parentsComputed: boolean;
private _types: string[] | undefined;
private _types: (string | undefined)[] | undefined;
constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, types?: string[]) {
constructor(startIndexes: Uint32Array, endIndexes: Uint32Array, types?: (string | undefined)[]) {
if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) {
throw new Error('invalid startIndexes or endIndexes size');
}
@@ -35,7 +33,7 @@ export class FoldingRegions {
private ensureParentIndices() {
if (!this._parentsComputed) {
this._parentsComputed = true;
let parentIndexes = [];
let parentIndexes: number[] = [];
let isInsideLast = (startLineNumber: number, endLineNumber: number) => {
let index = parentIndexes[parentIndexes.length - 1];
return this.getStartLineNumber(index) <= startLineNumber && this.getEndLineNumber(index) >= endLineNumber;
@@ -146,7 +144,7 @@ export class FoldingRegions {
}
public toString() {
let res = [];
let res: string[] = [];
for (let i = 0; i < this.length; i++) {
res[i] = `[${this.isCollapsed(i) ? '+' : '-'}] ${this.getStartLineNumber(i)}/${this.getEndLineNumber(i)}`;
}

View File

@@ -13,7 +13,7 @@ import { findFirstInSorted } from 'vs/base/common/arrays';
export class HiddenRangeModel {
private _foldingModel: FoldingModel;
private _hiddenRanges: IRange[];
private _foldingModelListener: IDisposable;
private _foldingModelListener: IDisposable | null;
private _updateEventEmitter = new Emitter<IRange[]>();
public get onDidChange(): Event<IRange[]> { return this._updateEventEmitter.event; }
@@ -70,7 +70,7 @@ export class HiddenRangeModel {
if (!Array.isArray(state) || state.length === 0) {
return false;
}
let hiddenRanges = [];
let hiddenRanges: IRange[] = [];
for (let r of state) {
if (!r.startLineNumber || !r.endLineNumber) {
return false;
@@ -104,7 +104,7 @@ export class HiddenRangeModel {
public adjustSelections(selections: Selection[]): boolean {
let hasChanges = false;
let editorModel = this._foldingModel.textModel;
let lastRange = null;
let lastRange: IRange | null = null;
let adjustLine = (line: number) => {
if (!lastRange || !isInside(line, lastRange)) {
@@ -148,7 +148,7 @@ export class HiddenRangeModel {
function isInside(line: number, range: IRange) {
return line >= range.startLineNumber && line <= range.endLineNumber;
}
function findRange(ranges: IRange[], line: number): IRange {
function findRange(ranges: IRange[], line: number): IRange | null {
let i = findFirstInSorted(ranges, r => line < r.startLineNumber) - 1;
if (i >= 0 && ranges[i].endLineNumber >= line) {
return ranges[i];

View File

@@ -3,14 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITextModel } from 'vs/editor/common/model';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
import { FoldingRegions, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges';
import { TextModel } from 'vs/editor/common/model/textModel';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -31,9 +28,9 @@ export class IndentRangeProvider implements RangeProvider {
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(this.editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let offSide = foldingRules && !!foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
return TPromise.as(computeRanges(this.editorModel, offSide, markers));
return Promise.resolve(computeRanges(this.editorModel, offSide, markers));
}
}
@@ -116,7 +113,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
const tabSize = model.getOptions().tabSize;
let result = new RangesCollector(foldingRangesLimit);
let pattern = void 0;
let pattern: RegExp | undefined = void 0;
if (markers) {
pattern = new RegExp(`(${markers.start.source})|(?:${markers.end.source})`);
}

View File

@@ -3,12 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { FoldingRegions, ILineRange } from 'vs/editor/contrib/folding/foldingRanges';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider';
@@ -18,7 +15,7 @@ export class InitializingRangeProvider implements RangeProvider {
readonly id = ID_INIT_PROVIDER;
private decorationIds: string[] | undefined;
private timeout: number;
private timeout: any;
constructor(private editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) {
if (initialRanges.length) {
@@ -61,7 +58,7 @@ export class InitializingRangeProvider implements RangeProvider {
}
}
}
return TPromise.as(sanitizeRanges(foldingRangeData, Number.MAX_VALUE));
return Promise.resolve(sanitizeRanges(foldingRangeData, Number.MAX_VALUE));
}
}

View File

@@ -3,14 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { FoldingRangeProvider, FoldingRange, FoldingContext } from 'vs/editor/common/modes';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { toThenable } from 'vs/base/common/async';
import { ITextModel } from 'vs/editor/common/model';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -32,7 +28,7 @@ export class SyntaxRangeProvider implements RangeProvider {
constructor(private editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) {
}
compute(cancellationToken: CancellationToken): Thenable<FoldingRegions> {
compute(cancellationToken: CancellationToken): Thenable<FoldingRegions | null> {
return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => {
if (ranges) {
let res = sanitizeRanges(ranges, this.limit);
@@ -48,9 +44,9 @@ export class SyntaxRangeProvider implements RangeProvider {
}
function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextModel, cancellationToken: CancellationToken): Thenable<IFoldingRangeData[] | null> {
let rangeData: IFoldingRangeData[] = null;
let rangeData: IFoldingRangeData[] | null = null;
let promises = providers.map((provider, i) => {
return toThenable(provider.provideFoldingRanges(model, foldingContext, cancellationToken)).then(ranges => {
return Promise.resolve(provider.provideFoldingRanges(model, foldingContext, cancellationToken)).then(ranges => {
if (cancellationToken.isCancellationRequested) {
return;
}
@@ -67,7 +63,7 @@ function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextMode
}
}, onUnexpectedExternalError);
});
return TPromise.join(promises).then(_ => {
return Promise.all(promises).then(_ => {
return rangeData;
});
}
@@ -77,7 +73,7 @@ export class RangesCollector {
private _endIndexes: number[];
private _nestingLevels: number[];
private _nestingLevelCounts: number[];
private _types: string[];
private _types: (string | undefined)[];
private _length: number;
private _foldingRangesLimit: number;
@@ -91,7 +87,7 @@ export class RangesCollector {
this._foldingRangesLimit = foldingRangesLimit;
}
public add(startLineNumber: number, endLineNumber: number, type: string, nestingLevel: number) {
public add(startLineNumber: number, endLineNumber: number, type: string | undefined, nestingLevel: number) {
if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
return;
}
@@ -131,7 +127,7 @@ export class RangesCollector {
let startIndexes = new Uint32Array(this._foldingRangesLimit);
let endIndexes = new Uint32Array(this._foldingRangesLimit);
let types = [];
let types: (string | undefined)[] = [];
for (let i = 0, k = 0; i < this._length; i++) {
let level = this._nestingLevels[i];
if (level < maxLevel || (level === maxLevel && entries++ < this._foldingRangesLimit)) {
@@ -159,8 +155,8 @@ export function sanitizeRanges(rangeData: IFoldingRangeData[], limit: number): F
});
let collector = new RangesCollector(limit);
let top: IFoldingRangeData = null;
let previous = [];
let top: IFoldingRangeData | undefined = void 0;
let previous: IFoldingRangeData[] = [];
for (let entry of sorted) {
if (!top) {
top = entry;
@@ -187,4 +183,4 @@ export function sanitizeRanges(rangeData: IFoldingRangeData[], limit: number): F
}
}
return collector.toIndentRanges();
}
}

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel';
import { TextModel, ModelDecorationOptions } from 'vs/editor/common/model/textModel';
@@ -40,7 +38,7 @@ export class TestDecorationProvider {
return this.model.deltaDecorations(oldDecorations, newDecorations);
}
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T {
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): (T | null) {
return this.model.changeDecorations(callback);
}
}
@@ -50,9 +48,9 @@ suite('Folding Model', () => {
return { startLineNumber, endLineNumber, isCollapsed };
}
function assertRegion(actual: FoldingRegion, expected: ExpectedRegion, message?: string) {
function assertRegion(actual: FoldingRegion | null, expected: ExpectedRegion | null, message?: string) {
assert.equal(!!actual, !!expected, message);
if (actual) {
if (actual && expected) {
assert.equal(actual.startLineNumber, expected.startLineNumber, message);
assert.equal(actual.endLineNumber, expected.endLineNumber, message);
assert.equal(actual.isCollapsed, expected.isCollapsed, message);
@@ -60,7 +58,7 @@ suite('Folding Model', () => {
}
function assertFoldedRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
let actualRanges = [];
let actualRanges: ExpectedRegion[] = [];
let actual = foldingModel.regions;
for (let i = 0; i < actual.length; i++) {
if (actual.isCollapsed(i)) {
@@ -71,7 +69,7 @@ suite('Folding Model', () => {
}
function assertRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
let actualRanges = [];
let actualRanges: ExpectedRegion[] = [];
let actual = foldingModel.regions;
for (let i = 0; i < actual.length; i++) {
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.isCollapsed(i)));
@@ -98,7 +96,7 @@ suite('Folding Model', () => {
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
let ranges = computeRanges(textModel, false, void 0);
foldingModel.update(ranges);
let r1 = r(1, 3, false);
@@ -137,7 +135,7 @@ suite('Folding Model', () => {
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
let ranges = computeRanges(textModel, false, void 0);
foldingModel.update(ranges);
let r1 = r(1, 3, false);
@@ -146,17 +144,17 @@ suite('Folding Model', () => {
assertRanges(foldingModel, [r1, r2, r3]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!]);
foldingModel.update(ranges);
assertRanges(foldingModel, [r(1, 3, true), r2, r3]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(5)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(5)!]);
foldingModel.update(ranges);
assertRanges(foldingModel, [r(1, 3, true), r2, r(5, 6, true)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(7)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(7)!]);
foldingModel.update(ranges);
assertRanges(foldingModel, [r(1, 3, true), r(4, 7, true), r(5, 6, true)]);
@@ -183,7 +181,7 @@ suite('Folding Model', () => {
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
let ranges = computeRanges(textModel, false, void 0);
foldingModel.update(ranges);
let r1 = r(1, 3, false);
@@ -191,11 +189,11 @@ suite('Folding Model', () => {
let r3 = r(5, 6, false);
assertRanges(foldingModel, [r1, r2, r3]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2), foldingModel.getRegionAtLine(5)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2)!, foldingModel.getRegionAtLine(5)!]);
textModel.applyEdits([EditOperation.insert(new Position(4, 1), '//hello\n')]);
foldingModel.update(computeRanges(textModel, false, null));
foldingModel.update(computeRanges(textModel, false, void 0));
assertRanges(foldingModel, [r(1, 3, true), r(5, 8, false), r(6, 7, true)]);
} finally {
@@ -223,7 +221,7 @@ suite('Folding Model', () => {
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
let ranges = computeRanges(textModel, false, void 0);
foldingModel.update(ranges);
let r1 = r(1, 12, false);
@@ -233,11 +231,11 @@ suite('Folding Model', () => {
let r5 = r(9, 11, false);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(6)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(6)!]);
textModel.applyEdits([EditOperation.delete(new Range(6, 11, 9, 0))]);
foldingModel.update(computeRanges(textModel, false, null));
foldingModel.update(computeRanges(textModel, false, void 0));
assertRanges(foldingModel, [r(1, 9, false), r(2, 8, false), r(3, 5, false), r(6, 8, false)]);
} finally {
@@ -260,7 +258,7 @@ suite('Folding Model', () => {
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
let ranges = computeRanges(textModel, false, void 0);
foldingModel.update(ranges);
let r1 = r(1, 3, false);

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
@@ -20,7 +18,7 @@ let markers: FoldingMarkers = {
suite('FoldingRanges', () => {
test('test max folding regions', () => {
let lines = [];
let lines: string[] = [];
let nRegions = MAX_FOLDING_REGIONS;
for (let i = 0; i < nRegions; i++) {
lines.push('#region');
@@ -85,7 +83,7 @@ suite('FoldingRanges', () => {
});
test('setCollapsed', () => {
let lines = [];
let lines: string[] = [];
let nRegions = 500;
for (let i = 0; i < nRegions; i++) {
lines.push('#region');

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { FoldingModel } from 'vs/editor/contrib/folding/foldingModel';
import { TextModel } from 'vs/editor/common/model/textModel';
@@ -46,10 +44,10 @@ suite('Hidden Range Model', () => {
assert.equal(hiddenRangeModel.hasRanges(), false);
let ranges = computeRanges(textModel, false, null);
let ranges = computeRanges(textModel, false, void 0);
foldingModel.update(ranges);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1), foldingModel.getRegionAtLine(6)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!, foldingModel.getRegionAtLine(6)!]);
assertRanges(hiddenRangeModel.hiddenRanges, [r(2, 3), r(7, 7)]);
assert.equal(hiddenRangeModel.hasRanges(), true);
@@ -64,7 +62,7 @@ suite('Hidden Range Model', () => {
assert.equal(hiddenRangeModel.isHidden(9), false);
assert.equal(hiddenRangeModel.isHidden(10), false);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(4)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(4)!]);
assertRanges(hiddenRangeModel.hiddenRanges, [r(2, 3), r(5, 9)]);
assert.equal(hiddenRangeModel.hasRanges(), true);
@@ -79,7 +77,7 @@ suite('Hidden Range Model', () => {
assert.equal(hiddenRangeModel.isHidden(9), true);
assert.equal(hiddenRangeModel.isHidden(10), false);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1), foldingModel.getRegionAtLine(6), foldingModel.getRegionAtLine(4)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)!, foldingModel.getRegionAtLine(6)!, foldingModel.getRegionAtLine(4)!]);
assertRanges(hiddenRangeModel.hiddenRanges, []);
assert.equal(hiddenRangeModel.hasRanges(), false);
assert.equal(hiddenRangeModel.isHidden(1), false);

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { TextModel } from 'vs/editor/common/model/textModel';
@@ -52,9 +50,9 @@ suite('Indentation Folding', () => {
let model = TextModel.createFromString(lines.join('\n'));
function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
let indentRanges = computeRanges(model, true, null, maxEntries);
let indentRanges = computeRanges(model, true, undefined, maxEntries);
assert.ok(indentRanges.length <= maxEntries, 'max ' + message);
let actual = [];
let actual: IndentRange[] = [];
for (let i = 0; i < indentRanges.length; i++) {
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
}

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
@@ -20,7 +18,7 @@ function assertRanges(lines: string[], expected: ExpectedIndentRange[], offside:
let model = TextModel.createFromString(lines.join('\n'));
let actual = computeRanges(model, offside, markers);
let actualRanges = [];
let actualRanges: ExpectedIndentRange[] = [];
for (let i = 0; i < actual.length; i++) {
actualRanges[i] = r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.getParentIndex(i));
}

View File

@@ -2,12 +2,10 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/syntaxRangeProvider';
import { FoldingRangeProvider, FoldingRange, FoldingContext } from 'vs/editor/common/modes';
import { FoldingRangeProvider, FoldingRange, FoldingContext, ProviderResult } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -20,7 +18,7 @@ class TestFoldingRangeProvider implements FoldingRangeProvider {
constructor(private model: ITextModel, private ranges: IndentRange[]) {
}
provideFoldingRanges(model: ITextModel, context: FoldingContext, token: CancellationToken): FoldingRange[] {
provideFoldingRanges(model: ITextModel, context: FoldingContext, token: CancellationToken): ProviderResult<FoldingRange[]> {
if (model === this.model) {
return this.ranges;
}
@@ -77,9 +75,11 @@ suite('Syntax folding', () => {
async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
let indentRanges = await new SyntaxRangeProvider(model, providers, maxEntries).compute(CancellationToken.None);
let actual = [];
for (let i = 0; i < indentRanges.length; i++) {
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
let actual: IndentRange[] = [];
if (indentRanges) {
for (let i = 0; i < indentRanges.length; i++) {
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
}
}
assert.deepEqual(actual, expectedRanges, message);
}

View File

@@ -2,11 +2,10 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { EditorZoom } from 'vs/editor/common/config/editorZoom';
class EditorFontZoomIn extends EditorAction {

View File

@@ -4,16 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { TPromise } from 'vs/base/common/winjs.base';
import { URI } from 'vs/base/common/uri';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { registerDefaultLanguageCommand, registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { asWinJsPromise, first } from 'vs/base/common/async';
import { first } from 'vs/base/common/async';
import { Position } from 'vs/editor/common/core/position';
import { CancellationToken } from 'vs/base/common/cancellation';
export class NoProviderError extends Error {
@@ -22,50 +22,50 @@ export class NoProviderError extends Error {
constructor(message?: string) {
super();
this.name = NoProviderError.Name;
this.message = message;
if (message) {
this.message = message;
}
}
}
export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions): TPromise<TextEdit[], NoProviderError> {
export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[] | undefined | null> {
const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model);
if (providers.length === 0) {
return TPromise.wrapError(new NoProviderError());
return Promise.reject(new NoProviderError());
}
return first(providers.map(provider => () => {
return asWinJsPromise(token => provider.provideDocumentRangeFormattingEdits(model, range, options, token))
return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token))
.then(undefined, onUnexpectedExternalError);
}), result => !isFalsyOrEmpty(result));
}), isNonEmptyArray);
}
export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions): TPromise<TextEdit[]> {
export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[] | null | undefined> {
const providers = DocumentFormattingEditProviderRegistry.ordered(model);
// try range formatters when no document formatter is registered
if (providers.length === 0) {
return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options);
return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token);
}
return first(providers.map(provider => () => {
return asWinJsPromise(token => provider.provideDocumentFormattingEdits(model, options, token))
return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token))
.then(undefined, onUnexpectedExternalError);
}), result => !isFalsyOrEmpty(result));
}), isNonEmptyArray);
}
export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): TPromise<TextEdit[]> {
export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): Promise<TextEdit[] | null | undefined> {
const [support] = OnTypeFormattingEditProviderRegistry.ordered(model);
if (!support) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
if (support.autoFormatTriggerCharacters.indexOf(ch) < 0) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
return asWinJsPromise((token) => {
return support.provideOnTypeFormattingEdits(model, position, ch, options, token);
}).then(r => r, onUnexpectedExternalError);
return Promise.resolve(support.provideOnTypeFormattingEdits(model, position, ch, options, CancellationToken.None)).then(r => r, onUnexpectedExternalError);
}
registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) {
@@ -77,7 +77,7 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args)
if (!model) {
throw illegalArgument('resource');
}
return getDocumentRangeFormattingEdits(model, Range.lift(range), options);
return getDocumentRangeFormattingEdits(model, Range.lift(range), options, CancellationToken.None);
});
registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) {
@@ -90,7 +90,7 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar
throw illegalArgument('resource');
}
return getDocumentFormattingEdits(model, options);
return getDocumentFormattingEdits(model, options, CancellationToken.None);
});
registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) {

View File

@@ -2,13 +2,11 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution, IActionOptions } from 'vs/editor/browser/editorExtensions';
@@ -27,6 +25,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { CancellationToken } from 'vs/base/common/cancellation';
function alertFormattingEdits(edits: ISingleEditOperation[]): void {
@@ -239,7 +238,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
const { tabSize, insertSpaces } = model.getOptions();
const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }).then(edits => {
getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }, CancellationToken.None).then(edits => {
return this.workerService.computeMoreMinimalEdits(model.uri, edits);
}).then(edits => {
if (!state.validate(this.editor) || isFalsyOrEmpty(edits)) {
@@ -262,14 +261,14 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
export abstract class AbstractFormatAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const workerService = accessor.get(IEditorWorkerService);
const notificationService = accessor.get(INotificationService);
const formattingPromise = this._getFormattingEdits(editor);
const formattingPromise = this._getFormattingEdits(editor, CancellationToken.None);
if (!formattingPromise) {
return TPromise.as(void 0);
return Promise.resolve(void 0);
}
// Capture the state of the editor
@@ -284,6 +283,7 @@ export abstract class AbstractFormatAction extends EditorAction {
FormattingEdit.execute(editor, edits);
alertFormattingEdits(edits);
editor.focus();
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate);
}, err => {
if (err instanceof Error && err.name === NoProviderError.Name) {
this._notifyNoProviderError(notificationService, editor.getModel().getLanguageIdentifier().language);
@@ -293,7 +293,8 @@ export abstract class AbstractFormatAction extends EditorAction {
});
}
protected abstract _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]>;
protected abstract _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]>;
protected _notifyNoProviderError(notificationService: INotificationService, language: string): void {
notificationService.info(nls.localize('no.provider', "There is no formatter for '{0}'-files installed.", language));
}
@@ -322,10 +323,10 @@ export class FormatDocumentAction extends AbstractFormatAction {
});
}
protected _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]> {
protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]> {
const model = editor.getModel();
const { tabSize, insertSpaces } = model.getOptions();
return getDocumentFormattingEdits(model, { tabSize, insertSpaces });
return getDocumentFormattingEdits(model, { tabSize, insertSpaces }, token);
}
protected _notifyNoProviderError(notificationService: INotificationService, language: string): void {
@@ -340,7 +341,7 @@ export class FormatSelectionAction extends AbstractFormatAction {
id: 'editor.action.formatSelection',
label: nls.localize('formatSelection.label', "Format Selection"),
alias: 'Format Code',
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasNonEmptySelection),
precondition: ContextKeyExpr.and(EditorContextKeys.writable),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F),
@@ -354,10 +355,16 @@ export class FormatSelectionAction extends AbstractFormatAction {
});
}
protected _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]> {
protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]> {
const model = editor.getModel();
let selection = editor.getSelection();
if (selection.isEmpty()) {
const maxColumn = model.getLineMaxColumn(selection.startLineNumber);
selection = selection.setStartPosition(selection.startLineNumber, 1);
selection = selection.setEndPosition(selection.endLineNumber, maxColumn);
}
const { tabSize, insertSpaces } = model.getOptions();
return getDocumentRangeFormattingEdits(model, editor.getSelection(), { tabSize, insertSpaces });
return getDocumentRangeFormattingEdits(model, selection, { tabSize, insertSpaces }, token);
}
protected _notifyNoProviderError(notificationService: INotificationService, language: string): void {
@@ -379,14 +386,14 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => {
constructor() {
super({} as IActionOptions);
}
_getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]> {
_getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]> {
const model = editor.getModel();
const editorSelection = editor.getSelection();
const { tabSize, insertSpaces } = model.getOptions();
return editorSelection.isEmpty()
? getDocumentFormattingEdits(model, { tabSize, insertSpaces })
: getDocumentRangeFormattingEdits(model, editorSelection, { tabSize, insertSpaces });
? getDocumentFormattingEdits(model, { tabSize, insertSpaces }, token)
: getDocumentRangeFormattingEdits(model, editorSelection, { tabSize, insertSpaces }, token);
}
}().run(accessor, editor);
}

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditOperation } from 'vs/editor/common/core/editOperation';
@@ -13,7 +12,7 @@ import { TextEdit } from 'vs/editor/common/modes';
export class FormattingEdit {
private static _handleEolEdits(editor: ICodeEditor, edits: TextEdit[]): ISingleEditOperation[] {
let newEol: EndOfLineSequence = undefined;
let newEol: EndOfLineSequence | undefined = undefined;
let singleEdits: ISingleEditOperation[] = [];
for (let edit of edits) {
@@ -26,13 +25,18 @@ export class FormattingEdit {
}
if (typeof newEol === 'number') {
editor.getModel().pushEOL(newEol);
if (editor.hasModel()) {
editor.getModel().pushEOL(newEol);
}
}
return singleEdits;
}
private static _isFullModelReplaceEdit(editor: ICodeEditor, edit: ISingleEditOperation): boolean {
if (!editor.hasModel()) {
return false;
}
const model = editor.getModel();
const editRange = model.validateRange(edit.range);
const fullModelRange = model.getFullModelRange();

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./goToDefinitionMouse';
import { KeyCode } from 'vs/base/common/keyCodes';
import * as browser from 'vs/base/browser/browser';
@@ -99,8 +97,8 @@ function createOptions(multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'): C
export class ClickLinkGesture extends Disposable {
private readonly _onMouseMoveOrRelevantKeyDown: Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent]> = this._register(new Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent]>());
public readonly onMouseMoveOrRelevantKeyDown: Event<[ClickLinkMouseEvent, ClickLinkKeyboardEvent]> = this._onMouseMoveOrRelevantKeyDown.event;
private readonly _onMouseMoveOrRelevantKeyDown: Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent | null]> = this._register(new Emitter<[ClickLinkMouseEvent, ClickLinkKeyboardEvent | null]>());
public readonly onMouseMoveOrRelevantKeyDown: Event<[ClickLinkMouseEvent, ClickLinkKeyboardEvent | null]> = this._onMouseMoveOrRelevantKeyDown.event;
private readonly _onExecute: Emitter<ClickLinkMouseEvent> = this._register(new Emitter<ClickLinkMouseEvent>());
public readonly onExecute: Event<ClickLinkMouseEvent> = this._onExecute.event;
@@ -111,7 +109,7 @@ export class ClickLinkGesture extends Disposable {
private readonly _editor: ICodeEditor;
private _opts: ClickLinkOptions;
private lastMouseMoveEvent: ClickLinkMouseEvent;
private lastMouseMoveEvent: ClickLinkMouseEvent | null;
private hasTriggerKeyOnMouseDown: boolean;
constructor(editor: ICodeEditor) {

View File

@@ -4,57 +4,61 @@
*--------------------------------------------------------------------------------------------*/
import { flatten, coalesce } from 'vs/base/common/arrays';
import { asWinJsPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { ITextModel } from 'vs/editor/common/model';
import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry } from 'vs/editor/common/modes';
import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry';
import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry } from 'vs/editor/common/modes';
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
function getDefinitions<T>(
model: ITextModel,
position: Position,
registry: LanguageFeatureRegistry<T>,
provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => DefinitionLink | DefinitionLink[] | Thenable<DefinitionLink | DefinitionLink[]>
): TPromise<DefinitionLink[]> {
provide: (provider: T, model: ITextModel, position: Position) => DefinitionLink | DefinitionLink[] | null | undefined | Thenable<DefinitionLink | DefinitionLink[] | null | undefined>
): Thenable<DefinitionLink[]> {
const provider = registry.ordered(model);
// get results
const promises = provider.map((provider): TPromise<DefinitionLink | DefinitionLink[]> => {
return asWinJsPromise((token) => {
return provide(provider, model, position, token);
}).then(undefined, err => {
const promises = provider.map((provider): Thenable<DefinitionLink | DefinitionLink[] | null | undefined> => {
return Promise.resolve(provide(provider, model, position)).then(undefined, err => {
onUnexpectedExternalError(err);
return null;
});
});
return TPromise.join(promises)
return Promise.all(promises)
.then(flatten)
.then(references => coalesce(references));
.then(coalesce);
}
export function getDefinitionsAtPosition(model: ITextModel, position: Position): TPromise<DefinitionLink[]> {
return getDefinitions(model, position, DefinitionProviderRegistry, (provider, model, position, token) => {
export function getDefinitionsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getDefinitions(model, position, DefinitionProviderRegistry, (provider, model, position) => {
return provider.provideDefinition(model, position, token);
});
}
export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise<DefinitionLink[]> {
return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position, token) => {
export function getDeclarationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getDefinitions(model, position, DeclarationProviderRegistry, (provider, model, position) => {
return provider.provideDeclaration(model, position, token);
});
}
export function getImplementationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position) => {
return provider.provideImplementation(model, position, token);
});
}
export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise<DefinitionLink[]> {
return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position, token) => {
export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position) => {
return provider.provideTypeDefinition(model, position, token);
});
}
registerDefaultLanguageCommand('_executeDefinitionProvider', getDefinitionsAtPosition);
registerDefaultLanguageCommand('_executeImplementationProvider', getImplementationsAtPosition);
registerDefaultLanguageCommand('_executeTypeDefinitionProvider', getTypeDefinitionsAtPosition);
registerDefaultLanguageCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None));
registerDefaultLanguageCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None));
registerDefaultLanguageCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None));
registerDefaultLanguageCommand('_executeTypeDefinitionProvider', (model, position) => getTypeDefinitionsAtPosition(model, position, CancellationToken.None));

View File

@@ -3,29 +3,32 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, IActionOptions, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import * as corePosition from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { registerEditorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { DefinitionLink } from 'vs/editor/common/modes';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDefinition';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { DefinitionLink, Location } from 'vs/editor/common/modes';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import { PeekContext } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController';
import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/referencesModel';
import { PeekContext } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
import * as nls from 'vs/nls';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import * as corePosition from 'vs/editor/common/core/position';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { createCancelablePromise } from 'vs/base/common/async';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition } from './goToDefinition';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
export class DefinitionActionConfig {
@@ -48,7 +51,7 @@ export class DefinitionAction extends EditorAction {
this._configuration = configuration;
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): Thenable<void> {
const notificationService = accessor.get(INotificationService);
const editorService = accessor.get(ICodeEditorService);
const progressService = accessor.get(IProgressService);
@@ -56,7 +59,7 @@ export class DefinitionAction extends EditorAction {
const model = editor.getModel();
const pos = editor.getPosition();
const definitionPromise = this._getDeclarationsAtPosition(model, pos).then(references => {
const definitionPromise = this._getTargetLocationForPosition(model, pos, CancellationToken.None).then(references => {
if (model.isDisposed() || editor.getModel() !== model) {
// new model, no more model
@@ -111,8 +114,8 @@ export class DefinitionAction extends EditorAction {
return definitionPromise;
}
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<DefinitionLink[]> {
return getDefinitionsAtPosition(model, position);
protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getDefinitionsAtPosition(model, position, token);
}
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
@@ -144,12 +147,11 @@ export class DefinitionAction extends EditorAction {
}
}
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: DefinitionLink, sideBySide: boolean): TPromise<ICodeEditor> {
const { uri, range } = reference;
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location, sideBySide: boolean): Thenable<ICodeEditor> {
return editorService.openCodeEditor({
resource: uri,
resource: reference.uri,
options: {
selection: Range.collapseToStart(range),
selection: Range.collapseToStart(reference.range),
revealIfOpened: true,
revealInCenterIfOutsideViewport: true
}
@@ -174,17 +176,17 @@ export class DefinitionAction extends EditorAction {
}
}
const goToDeclarationKb = platform.isWeb
const goToDefinitionKb = platform.isWeb
? KeyMod.CtrlCmd | KeyCode.F12
: KeyCode.F12;
export class GoToDefinitionAction extends DefinitionAction {
public static readonly ID = 'editor.action.goToDeclaration';
static readonly id = 'editor.action.revealDefinition';
constructor() {
super(new DefinitionActionConfig(), {
id: GoToDefinitionAction.ID,
id: GoToDefinitionAction.id,
label: nls.localize('actions.goToDecl.label', "Go to Definition"),
alias: 'Go to Definition',
precondition: ContextKeyExpr.and(
@@ -192,7 +194,7 @@ export class GoToDefinitionAction extends DefinitionAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: goToDeclarationKb,
primary: goToDefinitionKb,
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
@@ -200,16 +202,17 @@ export class GoToDefinitionAction extends DefinitionAction {
order: 1.1
}
});
CommandsRegistry.registerCommandAlias('editor.action.goToDeclaration', GoToDefinitionAction.id);
}
}
export class OpenDefinitionToSideAction extends DefinitionAction {
public static readonly ID = 'editor.action.openDeclarationToTheSide';
static readonly id = 'editor.action.revealDefinitionAside';
constructor() {
super(new DefinitionActionConfig(true), {
id: OpenDefinitionToSideAction.ID,
id: OpenDefinitionToSideAction.id,
label: nls.localize('actions.goToDeclToSide.label', "Open Definition to the Side"),
alias: 'Open Definition to the Side',
precondition: ContextKeyExpr.and(
@@ -217,17 +220,21 @@ export class OpenDefinitionToSideAction extends DefinitionAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb),
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDefinitionKb),
weight: KeybindingWeight.EditorContrib
}
});
CommandsRegistry.registerCommandAlias('editor.action.openDeclarationToTheSide', OpenDefinitionToSideAction.id);
}
}
export class PeekDefinitionAction extends DefinitionAction {
static readonly id = 'editor.action.peekDefinition';
constructor() {
super(new DefinitionActionConfig(void 0, true, false), {
id: 'editor.action.previewDeclaration',
id: PeekDefinitionAction.id,
label: nls.localize('actions.previewDecl.label', "Peek Definition"),
alias: 'Peek Definition',
precondition: ContextKeyExpr.and(
@@ -245,12 +252,78 @@ export class PeekDefinitionAction extends DefinitionAction {
order: 1.2
}
});
CommandsRegistry.registerCommandAlias('editor.action.previewDeclaration', PeekDefinitionAction.id);
}
}
export class DeclarationAction extends DefinitionAction {
protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getDeclarationsAtPosition(model, position, token);
}
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
return info && info.word
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
: nls.localize('decl.generic.noResults', "No declaration found");
}
protected _getMetaTitle(model: ReferencesModel): string {
return model.references.length > 1 && nls.localize('decl.meta.title', " {0} declarations", model.references.length);
}
}
export class GoToDeclarationAction extends DeclarationAction {
static readonly id = 'editor.action.revealDeclaration';
constructor() {
super(new DefinitionActionConfig(), {
id: GoToDeclarationAction.id,
label: nls.localize('actions.goToDeclaration.label', "Go to Declaration"),
alias: 'Go to Declaration',
precondition: ContextKeyExpr.and(
EditorContextKeys.hasDeclarationProvider,
EditorContextKeys.isInEmbeddedEditor.toNegated()),
menuOpts: {
group: 'navigation',
order: 1.3
}
});
}
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
return info && info.word
? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word)
: nls.localize('decl.generic.noResults', "No declaration found");
}
protected _getMetaTitle(model: ReferencesModel): string {
return model.references.length > 1 && nls.localize('decl.meta.title', " {0} declarations", model.references.length);
}
}
export class PeekDeclarationAction extends DeclarationAction {
constructor() {
super(new DefinitionActionConfig(void 0, true, false), {
id: 'editor.action.peekDeclaration',
label: nls.localize('actions.peekDecl.label', "Peek Declaration"),
alias: 'Peek Declaration',
precondition: ContextKeyExpr.and(
EditorContextKeys.hasDeclarationProvider,
PeekContext.notInPeekEditor,
EditorContextKeys.isInEmbeddedEditor.toNegated()),
menuOpts: {
group: 'navigation',
order: 1.31
}
});
}
}
export class ImplementationAction extends DefinitionAction {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<DefinitionLink[]> {
return getImplementationsAtPosition(model, position);
protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getImplementationsAtPosition(model, position, token);
}
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
@@ -307,8 +380,8 @@ export class PeekImplementationAction extends ImplementationAction {
}
export class TypeDefinitionAction extends DefinitionAction {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<DefinitionLink[]> {
return getTypeDefinitionsAtPosition(model, position);
protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable<DefinitionLink[]> {
return getTypeDefinitionsAtPosition(model, position, token);
}
protected _getNoResultFoundMessage(info?: IWordAtPosition): string {
@@ -371,7 +444,37 @@ export class PeekTypeDefinitionAction extends TypeDefinitionAction {
registerEditorAction(GoToDefinitionAction);
registerEditorAction(OpenDefinitionToSideAction);
registerEditorAction(PeekDefinitionAction);
registerEditorAction(GoToDeclarationAction);
registerEditorAction(PeekDeclarationAction);
registerEditorAction(GoToImplementationAction);
registerEditorAction(PeekImplementationAction);
registerEditorAction(GoToTypeDefinitionAction);
registerEditorAction(PeekTypeDefinitionAction);
// Go to menu
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: 'z_go_to',
command: {
id: 'editor.action.goToDeclaration',
title: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition")
},
order: 4
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: 'z_go_to',
command: {
id: 'editor.action.goToTypeDefinition',
title: nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition")
},
order: 5
});
MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
group: 'z_go_to',
command: {
id: 'editor.action.goToImplementation',
title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation")
},
order: 6
});

View File

@@ -3,14 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./goToDefinitionMouse';
import * as nls from 'vs/nls';
import { Throttler } from 'vs/base/common/async';
import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModeService } from 'vs/editor/common/services/modeService';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
@@ -25,7 +23,7 @@ import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegist
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { DefinitionAction, DefinitionActionConfig } from './goToDefinitionCommands';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture';
import { IWordAtPosition, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { IWordAtPosition, IModelDeltaDecoration, ITextModel, IFoundBracket } from 'vs/editor/common/model';
import { Position } from 'vs/editor/common/core/position';
class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution {
@@ -37,7 +35,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
private toUnhook: IDisposable[];
private decorations: string[];
private currentWordUnderMouse: IWordAtPosition;
private throttler: Throttler;
private previousPromise: CancelablePromise<DefinitionLink[]>;
constructor(
editor: ICodeEditor,
@@ -47,7 +45,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
this.toUnhook = [];
this.decorations = [];
this.editor = editor;
this.throttler = new Throttler();
this.previousPromise = null;
let linkGesture = new ClickLinkGesture(editor);
this.toUnhook.push(linkGesture);
@@ -58,7 +56,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
this.toUnhook.push(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => {
if (this.isEnabled(mouseEvent)) {
this.gotoDefinition(mouseEvent.target, mouseEvent.hasSideBySideModifier).done(() => {
this.gotoDefinition(mouseEvent.target, mouseEvent.hasSideBySideModifier).then(() => {
this.removeDecorations();
}, (error: Error) => {
this.removeDecorations();
@@ -75,6 +73,12 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}
private startFindDefinition(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void {
// check if we are active and on a content widget
if (mouseEvent.target.type === MouseTargetType.CONTENT_WIDGET && this.decorations.length > 0) {
return;
}
if (!this.isEnabled(mouseEvent, withKey)) {
this.currentWordUnderMouse = null;
this.removeDecorations();
@@ -100,12 +104,14 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
// Find definition and decorate word if found
let state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll);
this.throttler.queue(() => {
return state.validate(this.editor)
? this.findDefinition(mouseEvent.target)
: TPromise.wrap<DefinitionLink[]>(null);
if (this.previousPromise) {
this.previousPromise.cancel();
this.previousPromise = null;
}
}).then(results => {
this.previousPromise = createCancelablePromise(token => this.findDefinition(mouseEvent.target, token));
this.previousPromise.then(results => {
if (!results || !results.length || !state.validate(this.editor)) {
this.removeDecorations();
return;
@@ -137,7 +143,8 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
const { object: { textEditorModel } } = ref;
const { startLineNumber } = result.range;
if (textEditorModel.getLineMaxColumn(startLineNumber) === 0) {
if (startLineNumber < 1 || startLineNumber > textEditorModel.getLineCount()) {
// invalid range
ref.dispose();
return;
}
@@ -153,12 +160,12 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
this.addDecoration(
wordRange,
new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), previewValue)
new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri.fsPath), previewValue)
);
ref.dispose();
});
}
}).done(undefined, onUnexpectedError);
}).then(undefined, onUnexpectedError);
}
private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number) {
@@ -204,7 +211,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
private getPreviewRangeBasedOnBrackets(textEditorModel: ITextModel, startLineNumber: number) {
const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES);
const brackets = [];
const brackets: IFoundBracket[] = [];
let ignoreFirstEmpty = true;
let currentBracket = textEditorModel.findNextBracket(new Position(startLineNumber, 1));
@@ -269,21 +276,21 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean {
return this.editor.getModel() &&
mouseEvent.isNoneOrSingleMouseDown &&
mouseEvent.target.type === MouseTargetType.CONTENT_TEXT &&
(mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) &&
(mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) &&
DefinitionProviderRegistry.has(this.editor.getModel());
}
private findDefinition(target: IMouseTarget): TPromise<DefinitionLink[]> {
private findDefinition(target: IMouseTarget, token: CancellationToken): Thenable<DefinitionLink[]> {
const model = this.editor.getModel();
if (!model) {
return TPromise.as(null);
return Promise.resolve(null);
}
return getDefinitionsAtPosition(model, target.position);
return getDefinitionsAtPosition(model, target.position, token);
}
private gotoDefinition(target: IMouseTarget, sideBySide: boolean): TPromise<any> {
private gotoDefinition(target: IMouseTarget, sideBySide: boolean): Thenable<any> {
this.editor.setPosition(target.position);
const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: undefined, label: undefined, id: undefined, precondition: undefined });
return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor));
@@ -301,7 +308,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
registerEditorContribution(GotoDefinitionWithMouseEditorContribution);
registerThemingParticipant((theme, collector) => {
let activeLinkForeground = theme.getColor(editorActiveLinkForeground);
const activeLinkForeground = theme.getColor(editorActiveLinkForeground);
if (activeLinkForeground) {
collector.addRule(`.monaco-editor .goto-definition-link { color: ${activeLinkForeground} !important; }`);
}

View File

@@ -3,13 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { Emitter } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { Position } from 'vs/editor/common/core/position';
@@ -24,7 +22,6 @@ import { MarkerNavigationWidget } from './gotoErrorWidget';
import { compare } from 'vs/base/common/strings';
import { binarySearch } from 'vs/base/common/arrays';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { TPromise } from 'vs/base/common/winjs.base';
import { onUnexpectedError } from 'vs/base/common/errors';
class MarkerModel {
@@ -39,7 +36,7 @@ class MarkerModel {
constructor(editor: ICodeEditor, markers: IMarker[]) {
this._editor = editor;
this._markers = null;
this._markers = [];
this._nextIdx = -1;
this._toUnbind = [];
this._ignoreSelectionChange = false;
@@ -53,7 +50,7 @@ class MarkerModel {
if (this._ignoreSelectionChange) {
return;
}
if (this.currentMarker && Range.containsPosition(this.currentMarker, this._editor.getPosition())) {
if (this.currentMarker && this._editor.getPosition() && Range.containsPosition(this.currentMarker, this._editor.getPosition()!)) {
return;
}
this._nextIdx = -1;
@@ -96,14 +93,14 @@ class MarkerModel {
for (let i = 0; i < this._markers.length; i++) {
let range = Range.lift(this._markers[i]);
if (range.isEmpty()) {
const word = this._editor.getModel().getWordAtPosition(range.getStartPosition());
if (range.isEmpty() && this._editor.getModel()) {
const word = this._editor.getModel()!.getWordAtPosition(range.getStartPosition());
if (word) {
range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn);
}
}
if (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition())) {
if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) {
this._nextIdx = i;
found = true;
break;
@@ -118,7 +115,7 @@ class MarkerModel {
}
}
get currentMarker(): IMarker {
get currentMarker(): IMarker | undefined {
return this.canNavigate() ? this._markers[this._nextIdx] : undefined;
}
@@ -161,7 +158,7 @@ class MarkerModel {
return this._markers.length > 0;
}
public findMarkerAtPosition(pos: Position): IMarker {
public findMarkerAtPosition(pos: Position): IMarker | undefined {
for (const marker of this._markers) {
if (Range.containsPosition(marker, pos)) {
return marker;
@@ -192,8 +189,8 @@ class MarkerController implements editorCommon.IEditorContribution {
}
private _editor: ICodeEditor;
private _model: MarkerModel;
private _widget: MarkerNavigationWidget;
private _model: MarkerModel | null;
private _widget: MarkerNavigationWidget | null;
private _widgetVisible: IContextKey<boolean>;
private _disposeOnClose: IDisposable[] = [];
@@ -248,15 +245,22 @@ class MarkerController implements editorCommon.IEditorContribution {
this._disposeOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp()));
this._disposeOnClose.push(this._model.onCurrentMarkerChanged(marker => {
if (!marker) {
if (!marker || !this._model) {
this._cleanUp();
} else {
this._model.withoutWatchingEditorPosition(() => {
if (!this._widget || !this._model) {
return;
}
this._widget.showAtMarker(marker, this._model.indexOf(marker), this._model.total);
});
}
}));
this._disposeOnClose.push(this._model.onMarkerSetChanged(() => {
if (!this._widget || !this._widget.position || !this._model) {
return;
}
const marker = this._model.findMarkerAtPosition(this._widget.position);
if (marker) {
this._widget.updateMarker(marker);
@@ -276,15 +280,29 @@ class MarkerController implements editorCommon.IEditorContribution {
}
private _onMarkerChanged(changedResources: URI[]): void {
if (!changedResources.some(r => this._editor.getModel().uri.toString() === r.toString())) {
let editorModel = this._editor.getModel();
if (!editorModel) {
return;
}
if (!this._model) {
return;
}
if (!changedResources.some(r => editorModel!.uri.toString() === r.toString())) {
return;
}
this._model.setMarkers(this._getMarkers());
}
private _getMarkers(): IMarker[] {
let model = this._editor.getModel();
if (!model) {
return [];
}
return this._markerService.read({
resource: this._editor.getModel().uri,
resource: model.uri,
severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info
});
}
@@ -302,28 +320,33 @@ class MarkerNavigationAction extends EditorAction {
this._multiFile = multiFile;
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): Thenable<void> {
const markerService = accessor.get(IMarkerService);
const editorService = accessor.get(ICodeEditorService);
const controller = MarkerController.get(editor);
if (!controller) {
return undefined;
return Promise.resolve(void 0);
}
const model = controller.getOrCreateModel();
const atEdge = model.move(this._isNext, !this._multiFile);
if (!atEdge || !this._multiFile) {
return undefined;
return Promise.resolve(void 0);
}
// try with the next/prev file
let markers = markerService.read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }).sort(MarkerNavigationAction.compareMarker);
if (markers.length === 0) {
return undefined;
return Promise.resolve(void 0);
}
let oldMarker = model.currentMarker || <IMarker>{ resource: editor.getModel().uri, severity: MarkerSeverity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 };
let editorModel = editor.getModel();
if (!editorModel) {
return Promise.resolve(void 0);
}
let oldMarker = model.currentMarker || <IMarker>{ resource: editorModel!.uri, severity: MarkerSeverity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 };
let idx = binarySearch(markers, oldMarker, MarkerNavigationAction.compareMarker);
if (idx < 0) {
// find best match...
@@ -336,11 +359,11 @@ class MarkerNavigationAction extends EditorAction {
}
let newMarker = markers[idx];
if (newMarker.resource.toString() === editor.getModel().uri.toString()) {
if (newMarker.resource.toString() === editorModel!.uri.toString()) {
// the next `resource` is this resource which
// means we cycle within this file
model.move(this._isNext, true);
return undefined;
return Promise.resolve(void 0);
}
// close the widget for this editor-instance, open the resource

View File

@@ -6,7 +6,7 @@
/* marker zone */
.monaco-editor .marker-widget {
padding-left: 2px;
padding: 6px 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -33,7 +33,16 @@
user-select: text;
}
.monaco-editor .marker-widget .descriptioncontainer .message {
display: flex;
}
.monaco-editor .marker-widget .descriptioncontainer .message .source,
.monaco-editor .marker-widget .descriptioncontainer .message .code,
.monaco-editor .marker-widget .descriptioncontainer .filename {
cursor: pointer;
opacity: 0.6;
}
.monaco-editor .marker-widget .descriptioncontainer .filename {
cursor: pointer;
}

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./gotoErrorWidget';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
@@ -23,7 +21,7 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { getBaseLabel, getPathLabel } from 'vs/base/common/labels';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
class MessageWidget {
@@ -47,6 +45,7 @@ class MessageWidget {
domNode.setAttribute('role', 'alert');
this._messageBlock = document.createElement('div');
dom.addClass(this._messageBlock, 'message');
domNode.appendChild(this._messageBlock);
this._relatedBlock = document.createElement('div');
@@ -79,22 +78,14 @@ class MessageWidget {
dispose(this._disposables);
}
update({ source, message, relatedInformation }: IMarker): void {
update({ source, message, relatedInformation, code }: IMarker): void {
if (source) {
this._lines = 0;
this._longestLineLength = 0;
const indent = new Array(source.length + 3 + 1).join(' ');
const lines = message.split(/\r\n|\r|\n/g);
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
this._lines += 1;
this._lines = lines.length;
this._longestLineLength = 0;
for (const line of lines) {
this._longestLineLength = Math.max(line.length, this._longestLineLength);
if (i === 0) {
message = `[${source}] ${line}`;
} else {
message += `\n${indent}${line}`;
}
}
} else {
this._lines = 1;
@@ -103,7 +94,7 @@ class MessageWidget {
dom.clearNode(this._relatedBlock);
if (!isFalsyOrEmpty(relatedInformation)) {
if (isNonEmptyArray(relatedInformation)) {
this._relatedBlock.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * .66)}px`;
this._lines += 1;
@@ -129,8 +120,26 @@ class MessageWidget {
}
}
this._messageBlock.innerText = message;
this._editor.applyFontInfo(this._messageBlock);
dom.clearNode(this._messageBlock);
if (source) {
const sourceElement = document.createElement('div');
sourceElement.innerText = `[${source}] `;
dom.addClass(sourceElement, 'source');
this._editor.applyFontInfo(sourceElement);
this._messageBlock.appendChild(sourceElement);
}
const messageElement = document.createElement('div');
messageElement.innerText = message;
this._editor.applyFontInfo(messageElement);
this._messageBlock.appendChild(messageElement);
if (code) {
const codeElement = document.createElement('div');
codeElement.innerText = ` [${code}]`;
dom.addClass(codeElement, 'code');
this._editor.applyFontInfo(codeElement);
this._messageBlock.appendChild(codeElement);
}
const fontInfo = this._editor.getConfiguration().fontInfo;
const scrollWidth = Math.ceil(fontInfo.typicalFullwidthCharacterWidth * this._longestLineLength * 0.75);
const scrollHeight = fontInfo.lineHeight * this._lines;
@@ -155,7 +164,7 @@ export class MarkerNavigationWidget extends ZoneWidget {
private _message: MessageWidget;
private _callOnDispose: IDisposable[] = [];
private _severity: MarkerSeverity;
private _backgroundColor: Color;
private _backgroundColor: Color | null;
private _onDidSelectRelatedInformation = new Emitter<IRelatedInformation>();
readonly onDidSelectRelatedInformation: Event<IRelatedInformation> = this._onDidSelectRelatedInformation.event;
@@ -182,7 +191,7 @@ export class MarkerNavigationWidget extends ZoneWidget {
} else if (this._severity === MarkerSeverity.Info) {
colorId = editorMarkerNavigationInfo;
}
let frameColor = theme.getColor(colorId);
const frameColor = theme.getColor(colorId);
this.style({
arrowColor: frameColor,
frameColor: frameColor
@@ -191,7 +200,7 @@ export class MarkerNavigationWidget extends ZoneWidget {
protected _applyStyles(): void {
if (this._parentContainer) {
this._parentContainer.style.backgroundColor = this._backgroundColor.toString();
this._parentContainer.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : '';
}
super._applyStyles();
}
@@ -240,7 +249,8 @@ export class MarkerNavigationWidget extends ZoneWidget {
// show
let range = Range.lift(marker);
let position = range.containsPosition(this.editor.getPosition()) ? this.editor.getPosition() : range.getStartPosition();
const editorPosition = this.editor.getPosition();
let position = editorPosition && range.containsPosition(editorPosition) ? editorPosition : range.getStartPosition();
super.show(position, this.computeRequiredHeight());
this.editor.revealPositionInCenter(position, ScrollType.Smooth);

View File

@@ -4,12 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce } from 'vs/base/common/arrays';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { ITextModel } from 'vs/editor/common/model';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { ITextModel } from 'vs/editor/common/model';
import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes';
export function getHover(model: ITextModel, position: Position, token: CancellationToken): Promise<Hover[]> {
@@ -24,7 +24,7 @@ export function getHover(model: ITextModel, position: Position, token: Cancellat
});
});
return Promise.all(promises).then(values => coalesce(values));
return Promise.all(promises).then(coalesce);
}
registerDefaultLanguageCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None));

View File

@@ -3,30 +3,28 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./hover';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution, IScrollEvent } from 'vs/editor/common/editorCommon';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { ModesContentHoverWidget } from './modesContentHover';
import { ModesGlyphHoverWidget } from './modesGlyphHover';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { editorHoverHighlight, editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { IModeService } from 'vs/editor/common/services/modeService';
import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation';
import { ModesContentHoverWidget } from 'vs/editor/contrib/hover/modesContentHover';
import { ModesGlyphHoverWidget } from 'vs/editor/contrib/hover/modesGlyphHover';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
export class ModesHoverController implements IEditorContribution {
@@ -191,8 +189,8 @@ export class ModesHoverController implements IEditorContribution {
}
private _onKeyDown(e: IKeyboardEvent): void {
if (e.keyCode !== KeyCode.Ctrl && e.keyCode !== KeyCode.Alt && e.keyCode !== KeyCode.Meta) {
// Do not hide hover when Ctrl/Meta is pressed
if (e.keyCode !== KeyCode.Ctrl && e.keyCode !== KeyCode.Alt && e.keyCode !== KeyCode.Meta && e.keyCode !== KeyCode.Shift) {
// Do not hide hover when a modifier key is pressed
this._hideWidgets();
}
}
@@ -273,24 +271,24 @@ registerEditorAction(ShowHoverAction);
// theming
registerThemingParticipant((theme, collector) => {
let editorHoverHighlightColor = theme.getColor(editorHoverHighlight);
const editorHoverHighlightColor = theme.getColor(editorHoverHighlight);
if (editorHoverHighlightColor) {
collector.addRule(`.monaco-editor .hoverHighlight { background-color: ${editorHoverHighlightColor}; }`);
}
let hoverBackground = theme.getColor(editorHoverBackground);
const hoverBackground = theme.getColor(editorHoverBackground);
if (hoverBackground) {
collector.addRule(`.monaco-editor .monaco-editor-hover { background-color: ${hoverBackground}; }`);
}
let hoverBorder = theme.getColor(editorHoverBorder);
const hoverBorder = theme.getColor(editorHoverBorder);
if (hoverBorder) {
collector.addRule(`.monaco-editor .monaco-editor-hover { border: 1px solid ${hoverBorder}; }`);
collector.addRule(`.monaco-editor .monaco-editor-hover .hover-row:not(:first-child):not(:empty) { border-top: 1px solid ${hoverBorder.transparent(0.5)}; }`);
}
let link = theme.getColor(textLinkForeground);
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.monaco-editor .monaco-editor-hover a { color: ${link}; }`);
}
let codeBackground = theme.getColor(textCodeBlockBackground);
const codeBackground = theme.getColor(textCodeBlockBackground);
if (codeBackground) {
collector.addRule(`.monaco-editor .monaco-editor-hover code { background-color: ${codeBackground}; }`);
}

View File

@@ -2,11 +2,10 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RunOnceScheduler, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { CancelablePromise, RunOnceScheduler, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
export interface IHoverComputer<Result> {
@@ -48,8 +47,6 @@ export const enum HoverStartMode {
export class HoverOperation<Result> {
static HOVER_TIME = 300;
private _computer: IHoverComputer<Result>;
private _state: ComputeHoverOperationState;
private _hoverTime: number;
@@ -57,17 +54,17 @@ export class HoverOperation<Result> {
private _firstWaitScheduler: RunOnceScheduler;
private _secondWaitScheduler: RunOnceScheduler;
private _loadingMessageScheduler: RunOnceScheduler;
private _asyncComputationPromise: CancelablePromise<Result>;
private _asyncComputationPromise: CancelablePromise<Result> | null;
private _asyncComputationPromiseDone: boolean;
private _completeCallback: (r: Result) => void;
private _errorCallback: (err: any) => void;
private _errorCallback: ((err: any) => void) | null | undefined;
private _progressCallback: (progress: any) => void;
constructor(computer: IHoverComputer<Result>, success: (r: Result) => void, error: (err: any) => void, progress: (progress: any) => void) {
constructor(computer: IHoverComputer<Result>, success: (r: Result) => void, error: ((err: any) => void) | null | undefined, progress: (progress: any) => void, hoverTime: number) {
this._computer = computer;
this._state = ComputeHoverOperationState.IDLE;
this._hoverTime = HoverOperation.HOVER_TIME;
this._hoverTime = hoverTime;
this._firstWaitScheduler = new RunOnceScheduler(() => this._triggerAsyncComputation(), 0);
this._secondWaitScheduler = new RunOnceScheduler(() => this._triggerSyncComputation(), 0);
@@ -103,7 +100,7 @@ export class HoverOperation<Result> {
if (this._computer.computeAsync) {
this._asyncComputationPromiseDone = false;
this._asyncComputationPromise = createCancelablePromise(token => this._computer.computeAsync(token));
this._asyncComputationPromise = createCancelablePromise(token => this._computer.computeAsync!(token));
this._asyncComputationPromise.then((asyncResult: Result) => {
this._asyncComputationPromiseDone = true;
this._withAsyncResult(asyncResult);

View File

@@ -2,17 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { toggleClass } from 'vs/base/browser/dom';
import { Position } from 'vs/editor/common/core/position';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { Widget } from 'vs/base/browser/ui/widget';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { Widget } from 'vs/base/browser/ui/widget';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
export class ContentHoverWidget extends Widget implements editorBrowser.IContentWidget {
@@ -21,7 +21,8 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent
private _isVisible: boolean;
private _containerDomNode: HTMLElement;
private _domNode: HTMLElement;
protected _showAtPosition: Position;
protected _showAtPosition: Position | null;
protected _showAtRange: Range | null;
private _stoleFocus: boolean;
private scrollbar: DomScrollableElement;
private disposables: IDisposable[] = [];
@@ -72,6 +73,7 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent
this.updateMaxHeight();
this._editor.addContentWidget(this);
this._showAtPosition = null;
this._showAtRange = null;
}
public getId(): string {
@@ -82,9 +84,10 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent
return this._containerDomNode;
}
public showAt(position: Position, focus: boolean): void {
public showAt(position: Position, range: Range, focus: boolean): void {
// Position has changed
this._showAtPosition = new Position(position.lineNumber, position.column);
this._showAtPosition = position;
this._showAtRange = range;
this.isVisible = true;
this._editor.layoutContentWidget(this);
@@ -110,10 +113,11 @@ export class ContentHoverWidget extends Widget implements editorBrowser.IContent
}
}
public getPosition(): editorBrowser.IContentWidgetPosition {
public getPosition(): editorBrowser.IContentWidgetPosition | null {
if (this.isVisible) {
return {
position: this._showAtPosition,
range: this._showAtRange,
preference: [
editorBrowser.ContentWidgetPositionPreference.ABOVE,
editorBrowser.ContentWidgetPositionPreference.BELOW
@@ -229,7 +233,7 @@ export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWi
this.isVisible = false;
}
public getPosition(): editorBrowser.IOverlayWidgetPosition {
public getPosition(): editorBrowser.IOverlayWidgetPosition | null {
return null;
}

View File

@@ -2,28 +2,27 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { HoverProviderRegistry, Hover, IColor, DocumentColorProvider } from 'vs/editor/common/modes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { HoverOperation, IHoverComputer, HoverStartMode } from './hoverOperation';
import { ContentHoverWidget } from './hoverWidgets';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Color, RGBA } from 'vs/base/common/color';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { DocumentColorProvider, Hover, HoverProviderRegistry, IColor } from 'vs/editor/common/modes';
import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color';
import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/colorPickerWidget';
import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector';
import { Color, RGBA } from 'vs/base/common/color';
import { IDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color';
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { CancellationToken } from 'vs/base/common/cancellation';
const $ = dom.$;
class ColorHover {
@@ -41,7 +40,7 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
private _editor: ICodeEditor;
private _result: HoverPart[];
private _range: Range;
private _range: Range | null;
constructor(editor: ICodeEditor) {
this._editor = editor;
@@ -58,10 +57,14 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
}
computeAsync(token: CancellationToken): Promise<HoverPart[]> {
if (!this._editor.hasModel() || !this._range) {
return Promise.resolve([]);
}
const model = this._editor.getModel();
if (!HoverProviderRegistry.has(model)) {
return Promise.resolve(null);
return Promise.resolve([]);
}
return getHover(model, new Position(
@@ -71,6 +74,10 @@ class ModesContentComputer implements IHoverComputer<HoverPart[]> {
}
computeSync(): HoverPart[] {
if (!this._editor.hasModel() || !this._range) {
return [];
}
const lineNumber = this._range.startLineNumber;
if (lineNumber > this._editor.getModel().getLineCount()) {
@@ -158,14 +165,14 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
static readonly ID = 'editor.contrib.modesContentHoverWidget';
private _messages: HoverPart[];
private _lastRange: Range;
private _lastRange: Range | null;
private _computer: ModesContentComputer;
private _hoverOperation: HoverOperation<HoverPart[]>;
private _highlightDecorations: string[];
private _isChangingDecorations: boolean;
private _markdownRenderer: MarkdownRenderer;
private _shouldFocus: boolean;
private _colorPicker: ColorPickerWidget;
private _colorPicker: ColorPickerWidget | null;
private renderDisposable: IDisposable = Disposable.None;
@@ -176,6 +183,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
) {
super(ModesContentHoverWidget.ID, editor);
this._messages = [];
this._lastRange = null;
this._computer = new ModesContentComputer(this._editor);
this._highlightDecorations = [];
this._isChangingDecorations = false;
@@ -187,7 +196,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._computer,
result => this._withResult(result, true),
null,
result => this._withResult(result, false)
result => this._withResult(result, false),
this._editor.getConfiguration().contribInfo.hover.delay
);
this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.FOCUS, () => {
@@ -301,7 +311,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
// update column from which to show
let renderColumn = Number.MAX_VALUE;
let highlightRange = messages[0].range;
let highlightRange = Range.lift(messages[0].range);
let fragment = document.createDocumentFragment();
let isEmptyHoverContent = true;
@@ -361,6 +371,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
newRange = range.setEndPosition(range.endLineNumber, range.startColumn + model.presentation.label.length);
}
this._editor.pushUndoStop();
this._editor.executeEdits('colorpicker', textEdits);
if (model.presentation.additionalTextEdits) {
@@ -392,7 +403,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
const colorChangeListener = model.onDidChangeColor(updateColorPresentations);
this._colorPicker = widget;
this.showAt(new Position(renderRange.startLineNumber, renderColumn), this._shouldFocus);
this.showAt(range.getStartPosition(), range, this._shouldFocus);
this.updateContents(fragment);
this._colorPicker.layout();
@@ -404,7 +415,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
// show
if (!containColorPicker && !isEmptyHoverContent) {
this.showAt(new Position(renderRange.startLineNumber, renderColumn), this._shouldFocus);
this.showAt(new Position(renderRange.startLineNumber, renderColumn), highlightRange, this._shouldFocus);
this.updateContents(fragment);
}

View File

@@ -2,15 +2,14 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { HoverOperation, IHoverComputer, HoverStartMode } from './hoverOperation';
import { GlyphHoverWidget } from './hoverWidgets';
import { $ } from 'vs/base/browser/dom';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IMarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
import { GlyphHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
export interface IHoverMessage {
value: IMarkdownString;
@@ -47,6 +46,10 @@ class MarginComputer implements IHoverComputer<IHoverMessage[]> {
let lineDecorations = this._editor.getLineDecorations(this._lineNumber);
let result: IHoverMessage[] = [];
if (!lineDecorations) {
return result;
}
for (let i = 0, len = lineDecorations.length; i < len; i++) {
let d = lineDecorations[i];
@@ -54,9 +57,9 @@ class MarginComputer implements IHoverComputer<IHoverMessage[]> {
continue;
}
let hoverMessage = d.options.glyphMarginHoverMessage;
const hoverMessage = d.options.glyphMarginHoverMessage;
if (isEmptyMarkdownString(hoverMessage)) {
if (!hoverMessage || isEmptyMarkdownString(hoverMessage)) {
continue;
}
@@ -105,8 +108,9 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
this._hoverOperation = new HoverOperation(
this._computer,
(result: IHoverMessage[]) => this._withResult(result),
null,
(result: any) => this._withResult(result)
undefined,
(result: any) => this._withResult(result),
300
);
}
@@ -167,7 +171,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
messages.forEach((msg) => {
const renderedContents = this._markdownRenderer.render(msg.value);
this._renderDisposeables.push(renderedContents);
fragment.appendChild($('div.hover-row', null, renderedContents.element));
fragment.appendChild($('div.hover-row', undefined, renderedContents.element));
});
this.updateContents(fragment);

View File

@@ -2,11 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { TPromise } from 'vs/base/common/winjs.base';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
@@ -39,7 +37,7 @@ class InPlaceReplaceController implements IEditorContribution {
private readonly editor: ICodeEditor;
private readonly editorWorkerService: IEditorWorkerService;
private decorationIds: string[] = [];
private currentRequest: CancelablePromise<IInplaceReplaceSupportResult>;
private currentRequest: CancelablePromise<IInplaceReplaceSupportResult | null>;
private decorationRemover: CancelablePromise<void>;
constructor(
@@ -57,29 +55,31 @@ class InPlaceReplaceController implements IEditorContribution {
return InPlaceReplaceController.ID;
}
public run(source: string, up: boolean): Thenable<void> {
public run(source: string, up: boolean): Promise<void> | undefined {
// cancel any pending request
if (this.currentRequest) {
this.currentRequest.cancel();
}
let selection = this.editor.getSelection();
const editorSelection = this.editor.getSelection();
const model = this.editor.getModel();
const modelURI = model.uri;
if (!model || !editorSelection) {
return undefined;
}
let selection = editorSelection;
if (selection.startLineNumber !== selection.endLineNumber) {
// Can't accept multiline selection
return null;
}
const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
if (!this.editorWorkerService.canNavigateValueSet(modelURI)) {
return undefined;
}
this.currentRequest = createCancelablePromise(token => this.editorWorkerService.navigateValueSet(modelURI, selection, up));
const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
const modelURI = model.uri;
if (!this.editorWorkerService.canNavigateValueSet(modelURI)) {
return Promise.resolve(void 0);
}
this.currentRequest = createCancelablePromise(token => this.editorWorkerService.navigateValueSet(modelURI, selection!, up));
return this.currentRequest.then(result => {
@@ -96,7 +96,7 @@ class InPlaceReplaceController implements IEditorContribution {
// Selection
let editRange = Range.lift(result.range);
let highlightRange = result.range;
let diff = result.value.length - (selection.endColumn - selection.startColumn);
let diff = result.value.length - (selection!.endColumn - selection!.startColumn);
// highlight
highlightRange = {
@@ -106,11 +106,11 @@ class InPlaceReplaceController implements IEditorContribution {
endColumn: highlightRange.startColumn + result.value.length
};
if (diff > 1) {
selection = new Selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn + diff - 1);
selection = new Selection(selection!.startLineNumber, selection!.startColumn, selection!.endLineNumber, selection!.endColumn + diff - 1);
}
// Insert new text
const command = new InPlaceReplaceCommand(editRange, selection, result.value);
const command = new InPlaceReplaceCommand(editRange, selection!, result.value);
this.editor.pushUndoStop();
this.editor.executeCommand(source, command);
@@ -149,12 +149,12 @@ class InPlaceReplaceUp extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
let controller = InPlaceReplaceController.get(editor);
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> | undefined {
const controller = InPlaceReplaceController.get(editor);
if (!controller) {
return undefined;
return Promise.resolve(void 0);
}
return TPromise.wrap(controller.run(this.id, true));
return controller.run(this.id, true);
}
}
@@ -174,12 +174,12 @@ class InPlaceReplaceDown extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
let controller = InPlaceReplaceController.get(editor);
public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> | undefined {
const controller = InPlaceReplaceController.get(editor);
if (!controller) {
return undefined;
return Promise.resolve(void 0);
}
return TPromise.wrap(controller.run(this.id, false));
return controller.run(this.id, false);
}
}
@@ -188,7 +188,7 @@ registerEditorAction(InPlaceReplaceUp);
registerEditorAction(InPlaceReplaceDown);
registerThemingParticipant((theme, collector) => {
let border = theme.getColor(editorBracketMatchBorder);
const border = theme.getColor(editorBracketMatchBorder);
if (border) {
collector.addRule(`.monaco-editor.vs .valueSetReplacement { outline: solid 2px ${border}; }`);
}

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';

View File

@@ -5,24 +5,23 @@
import * as nls from 'vs/nls';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import * as strings from 'vs/base/common/strings';
import { IEditorContribution, ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, ServicesAccessor, IActionOptions, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import { TextEdit, StandardTokenType } from 'vs/editor/common/modes';
import * as IndentUtil from './indentUtils';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range, IRange } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IIdentifiedSingleEditOperation, ITextModel, EndOfLineSequence } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { StandardTokenType, TextEdit } from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules';
import { IModelService } from 'vs/editor/common/services/modelService';
import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
export function shiftIndent(tabSize: number, indentation: string, count?: number): string {
count = count || 1;
@@ -49,12 +48,12 @@ export function unshiftIndent(tabSize: number, indentation: string, count?: numb
export function getReindentEditOperations(model: ITextModel, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): IIdentifiedSingleEditOperation[] {
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
// Model is empty
return undefined;
return [];
}
let indentationRules = LanguageConfigurationRegistry.getIndentationRules(model.getLanguageIdentifier().id);
if (!indentationRules) {
return undefined;
return [];
}
endLineNumber = Math.min(endLineNumber, model.getLineCount());
@@ -74,11 +73,11 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu
}
if (startLineNumber > endLineNumber - 1) {
return undefined;
return [];
}
let { tabSize, insertSpaces } = model.getOptions();
let indentEdits = [];
let indentEdits: IIdentifiedSingleEditOperation[] = [];
// indentation being passed to lines below
let globalIndent: string;
@@ -168,7 +167,11 @@ export class IndentationToSpacesAction extends EditorAction {
return;
}
let modelOpts = model.getOptions();
const command = new IndentationToSpacesCommand(editor.getSelection(), modelOpts.tabSize);
let selection = editor.getSelection();
if (!selection) {
return;
}
const command = new IndentationToSpacesCommand(selection, modelOpts.tabSize);
editor.pushUndoStop();
editor.executeCommands(this.id, [command]);
@@ -198,7 +201,11 @@ export class IndentationToTabsAction extends EditorAction {
return;
}
let modelOpts = model.getOptions();
const command = new IndentationToTabsCommand(editor.getSelection(), modelOpts.tabSize);
let selection = editor.getSelection();
if (!selection) {
return;
}
const command = new IndentationToTabsCommand(selection, modelOpts.tabSize);
editor.pushUndoStop();
editor.executeCommands(this.id, [command]);
@@ -216,13 +223,13 @@ export class ChangeIndentationSizeAction extends EditorAction {
super(opts);
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const quickOpenService = accessor.get(IQuickOpenService);
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const quickInputService = accessor.get(IQuickInputService);
const modelService = accessor.get(IModelService);
let model = editor.getModel();
if (!model) {
return undefined;
return;
}
let creationOpts = modelService.getCreationOptions(model.getLanguageIdentifier().language, model.uri, model.isForSimpleWidget);
@@ -230,22 +237,24 @@ export class ChangeIndentationSizeAction extends EditorAction {
id: n.toString(),
label: n.toString(),
// add description for tabSize value set in the configuration
description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : null
description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : undefined
}));
// auto focus the tabSize set for the current editor
const autoFocusIndex = Math.min(model.getOptions().tabSize - 1, 7);
return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).then(() =>
quickOpenService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), autoFocus: { autoFocusIndex } }).then(pick => {
setTimeout(() => {
quickInputService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), activeItem: picks[autoFocusIndex] }).then(pick => {
if (pick) {
model.updateOptions({
tabSize: parseInt(pick.label, 10),
insertSpaces: this.insertSpaces
});
if (model && !model.isDisposed()) {
model.updateOptions({
tabSize: parseInt(pick.label, 10),
insertSpaces: this.insertSpaces
});
}
}
})
);
});
}, 50/* quick open is sensitive to being opened so soon after another */);
}
}
@@ -319,7 +328,7 @@ export class ReindentLinesAction extends EditorAction {
return;
}
let edits = getReindentEditOperations(model, 1, model.getLineCount());
if (edits) {
if (edits.length > 0) {
editor.pushUndoStop();
editor.executeEdits(this.id, edits);
editor.pushUndoStop();
@@ -343,9 +352,14 @@ export class ReindentSelectedLinesAction extends EditorAction {
return;
}
let selections = editor.getSelections();
if (selections === null) {
return;
}
let edits: IIdentifiedSingleEditOperation[] = [];
for (let selection of editor.getSelections()) {
for (let selection of selections) {
let startLineNumber = selection.startLineNumber;
let endLineNumber = selection.endLineNumber;
@@ -361,7 +375,7 @@ export class ReindentSelectedLinesAction extends EditorAction {
startLineNumber--;
}
let editOperations = getReindentEditOperations(model, startLineNumber, endLineNumber) || [];
let editOperations = getReindentEditOperations(model, startLineNumber, endLineNumber);
edits.push(...editOperations);
}
@@ -375,7 +389,7 @@ export class ReindentSelectedLinesAction extends EditorAction {
export class AutoIndentOnPasteCommand implements ICommand {
private _edits: TextEdit[];
private _edits: { range: IRange; text: string; eol?: EndOfLineSequence; }[];
private _initialSelection: Selection;
private _selectionId: string;
@@ -386,7 +400,7 @@ export class AutoIndentOnPasteCommand implements ICommand {
for (let edit of edits) {
if (edit.range && typeof edit.text === 'string') {
this._edits.push(edit);
this._edits.push(edit as { range: IRange; text: string; eol?: EndOfLineSequence; });
}
}
}
@@ -457,11 +471,16 @@ export class AutoIndentOnPaste implements IEditorContribution {
}
private trigger(range: Range): void {
if (this.editor.getSelections().length > 1) {
let selections = this.editor.getSelections();
if (selections === null || selections.length > 1) {
return;
}
const model = this.editor.getModel();
if (!model) {
return;
}
if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) {
return;
}
@@ -510,11 +529,11 @@ export class AutoIndentOnPaste implements IEditorContribution {
if (indentOfFirstLine !== null) {
let oldIndentation = strings.getLeadingWhitespace(firstLineText);
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
let newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let newIndent = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
let newIndent = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
textEdits.push({
range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1),
text: newIndent
@@ -566,17 +585,17 @@ export class AutoIndentOnPaste implements IEditorContribution {
};
let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, startLineNumber + 1, indentConverter);
if (indentOfSecondLine !== null) {
let newSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(indentOfSecondLine, tabSize);
let oldSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize);
let newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize);
let oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize);
if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize);
let originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces);
let newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces);
if (newIndent !== originalIndent) {
textEdits.push({
@@ -589,7 +608,7 @@ export class AutoIndentOnPaste implements IEditorContribution {
}
}
let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection());
let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!);
this.editor.executeCommand('autoIndentOnPaste', cmd);
this.editor.pushUndoStop();
}

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Range } from 'vs/editor/common/core/range';
import { Selection, SelectionDirection } from 'vs/editor/common/core/selection';

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';

View File

@@ -2,29 +2,28 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/sortLinesCommand';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { ICommand } from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';
import { registerEditorAction, ServicesAccessor, IActionOptions, EditorAction } from 'vs/editor/browser/editorExtensions';
import { CopyLinesCommand } from './copyLinesCommand';
import { DeleteLinesCommand } from './deleteLinesCommand';
import { MoveLinesCommand } from './moveLinesCommand';
import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { CopyLinesCommand } from 'vs/editor/contrib/linesOperations/copyLinesCommand';
import { DeleteLinesCommand } from 'vs/editor/contrib/linesOperations/deleteLinesCommand';
import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand';
import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/sortLinesCommand';
import { MenuId } from 'vs/platform/actions/common/actions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
// copy lines
@@ -40,7 +39,7 @@ abstract class AbstractCopyLinesAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
let commands: ICommand[] = [];
let selections = editor.getSelections();
let selections = editor.getSelections() || [];
for (let i = 0; i < selections.length; i++) {
commands.push(new CopyLinesCommand(selections[i], this.down));
@@ -112,7 +111,7 @@ abstract class AbstractMoveLinesAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
let commands: ICommand[] = [];
let selections = editor.getSelections();
let selections = editor.getSelections() || [];
let autoIndent = editor.getConfiguration().autoIndent;
for (let i = 0; i < selections.length; i++) {
@@ -180,7 +179,7 @@ export abstract class AbstractSortLinesAction extends EditorAction {
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
const selections = editor.getSelections();
const selections = editor.getSelections() || [];
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
@@ -247,10 +246,15 @@ export class TrimTrailingWhitespaceAction extends EditorAction {
// See https://github.com/editorconfig/editorconfig-vscode/issues/47
// It is very convenient for the editor config extension to invoke this action.
// So, if we get a reason:'auto-save' passed in, let's preserve cursor positions.
cursors = editor.getSelections().map(s => new Position(s.positionLineNumber, s.positionColumn));
cursors = (editor.getSelections() || []).map(s => new Position(s.positionLineNumber, s.positionColumn));
}
let command = new TrimTrailingWhitespaceCommand(editor.getSelection(), cursors);
let selection = editor.getSelection();
if (selection === null) {
return;
}
let command = new TrimTrailingWhitespaceCommand(selection, cursors);
editor.pushUndoStop();
editor.executeCommands(this.id, [command]);
@@ -266,7 +270,7 @@ interface IDeleteLinesOperation {
positionColumn: number;
}
class DeleteLinesAction extends EditorAction {
export class DeleteLinesAction extends EditorAction {
constructor() {
super({
@@ -298,7 +302,11 @@ class DeleteLinesAction extends EditorAction {
private _getLinesToRemove(editor: ICodeEditor): IDeleteLinesOperation[] {
// Construct delete operations
let operations: IDeleteLinesOperation[] = editor.getSelections().map((s) => {
let selections = editor.getSelections();
if (selections === null) {
return [];
}
let operations: IDeleteLinesOperation[] = selections.map((s) => {
let endLineNumber = s.endLineNumber;
if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) {
@@ -314,14 +322,17 @@ class DeleteLinesAction extends EditorAction {
// Sort delete operations
operations.sort((a, b) => {
if (a.startLineNumber === b.startLineNumber) {
return a.endLineNumber - b.endLineNumber;
}
return a.startLineNumber - b.startLineNumber;
});
// Merge delete operations on consecutive lines
// Merge delete operations which are adjacent or overlapping
let mergedOperations: IDeleteLinesOperation[] = [];
let previousOperation = operations[0];
for (let i = 1; i < operations.length; i++) {
if (previousOperation.endLineNumber + 1 === operations[i].startLineNumber) {
if (previousOperation.endLineNumber + 1 >= operations[i].startLineNumber) {
// Merge current operations into the previous one
previousOperation.endLineNumber = operations[i].endLineNumber;
} else {
@@ -353,8 +364,12 @@ export class IndentLinesAction extends EditorAction {
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
const cursors = editor._getCursors();
if (!cursors) {
return;
}
editor.pushUndoStop();
editor.executeCommands(this.id, TypeOperations.indent(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()));
editor.executeCommands(this.id, TypeOperations.indent(cursors.context.config, editor.getModel(), editor.getSelections()));
editor.pushUndoStop();
}
}
@@ -375,7 +390,7 @@ class OutdentLinesAction extends EditorAction {
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
CoreEditingCommands.Outdent.runEditorCommand(null, editor, null);
CoreEditingCommands.Outdent.runEditorCommand(_accessor, editor, null);
}
}
@@ -395,8 +410,12 @@ export class InsertLineBeforeAction extends EditorAction {
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
const cursors = editor._getCursors();
if (!cursors) {
return;
}
editor.pushUndoStop();
editor.executeCommands(this.id, TypeOperations.lineInsertBefore(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()));
editor.executeCommands(this.id, TypeOperations.lineInsertBefore(cursors.context.config, editor.getModel(), editor.getSelections()));
}
}
@@ -416,14 +435,22 @@ export class InsertLineAfterAction extends EditorAction {
}
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
const cursors = editor._getCursors();
if (!cursors) {
return;
}
editor.pushUndoStop();
editor.executeCommands(this.id, TypeOperations.lineInsertAfter(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()));
editor.executeCommands(this.id, TypeOperations.lineInsertAfter(cursors.context.config, editor.getModel(), editor.getSelections()));
}
}
export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
const primaryCursor = editor.getSelection();
if (primaryCursor === null) {
return;
}
let rangesToDelete = this._getRangesToDelete(editor);
// merge overlapping selections
let effectiveRanges: Range[] = [];
@@ -469,7 +496,7 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: null,
primary: 0,
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
weight: KeybindingWeight.EditorContrib
}
@@ -477,7 +504,7 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
}
_getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] {
let endPrimaryCursor: Selection;
let endPrimaryCursor: Selection | null = null;
let endCursorState: Selection[] = [];
let deletedLines = 0;
@@ -507,15 +534,24 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
}
_getRangesToDelete(editor: ICodeEditor): Range[] {
let rangesToDelete: Range[] = editor.getSelections();
let selections = editor.getSelections();
if (selections === null) {
return [];
}
let rangesToDelete: Range[] = selections;
let model = editor.getModel();
if (model === null) {
return [];
}
rangesToDelete.sort(Range.compareRangesUsingStarts);
rangesToDelete = rangesToDelete.map(selection => {
if (selection.isEmpty()) {
if (selection.startColumn === 1) {
let deleteFromLine = Math.max(1, selection.startLineNumber - 1);
let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model.getLineContent(deleteFromLine).length + 1;
let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model!.getLineContent(deleteFromLine).length + 1;
return new Range(deleteFromLine, deleteFromColumn, selection.startLineNumber, 1);
} else {
return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn);
@@ -538,7 +574,7 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction {
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: null,
primary: 0,
mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_K, secondary: [KeyMod.CtrlCmd | KeyCode.Delete] },
weight: KeybindingWeight.EditorContrib
}
@@ -546,7 +582,7 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction {
}
_getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] {
let endPrimaryCursor: Selection;
let endPrimaryCursor: Selection | null = null;
let endCursorState: Selection[] = [];
for (let i = 0, len = rangesToDelete.length, offset = 0; i < len; i++) {
let range = rangesToDelete[i];
@@ -568,10 +604,19 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction {
_getRangesToDelete(editor: ICodeEditor): Range[] {
let model = editor.getModel();
if (model === null) {
return [];
}
let rangesToDelete: Range[] = editor.getSelections().map((sel) => {
let selections = editor.getSelections();
if (selections === null) {
return [];
}
let rangesToDelete: Range[] = selections.map((sel) => {
if (sel.isEmpty()) {
const maxColumn = model.getLineMaxColumn(sel.startLineNumber);
const maxColumn = model!.getLineMaxColumn(sel.startLineNumber);
if (sel.startColumn === maxColumn) {
return new Range(sel.startLineNumber, sel.startColumn, sel.startLineNumber + 1, 1);
@@ -605,7 +650,14 @@ export class JoinLinesAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
let selections = editor.getSelections();
if (selections === null) {
return;
}
let primaryCursor = editor.getSelection();
if (primaryCursor === null) {
return;
}
selections.sort(Range.compareRangesUsingStarts);
let reducedSelections: Selection[] = [];
@@ -613,7 +665,7 @@ export class JoinLinesAction extends EditorAction {
let lastSelection = selections.reduce((previousValue, currentValue) => {
if (previousValue.isEmpty()) {
if (previousValue.endLineNumber === currentValue.startLineNumber) {
if (primaryCursor.equalsSelection(previousValue)) {
if (primaryCursor!.equalsSelection(previousValue)) {
primaryCursor = currentValue;
}
return currentValue;
@@ -638,8 +690,12 @@ export class JoinLinesAction extends EditorAction {
reducedSelections.push(lastSelection);
let model = editor.getModel();
let edits = [];
let endCursorState = [];
if (model === null) {
return;
}
let edits: IIdentifiedSingleEditOperation[] = [];
let endCursorState: Selection[] = [];
let endPrimaryCursor = primaryCursor;
let lineOffset = 0;
@@ -748,7 +804,15 @@ export class TransposeAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
let selections = editor.getSelections();
if (selections === null) {
return;
}
let model = editor.getModel();
if (model === null) {
return;
}
let commands: ICommand[] = [];
for (let i = 0, len = selections.length; i < len; i++) {
@@ -789,7 +853,15 @@ export class TransposeAction extends EditorAction {
export abstract class AbstractCaseAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
let selections = editor.getSelections();
if (selections === null) {
return;
}
let model = editor.getModel();
if (model === null) {
return;
}
let commands: ICommand[] = [];
for (let i = 0, len = selections.length; i < len; i++) {

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as strings from 'vs/base/common/strings';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { LanguageConfigurationRegistry, IIndentConverter } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import * as IndentUtil from 'vs/editor/contrib/indentation/indentUtils';
import { IndentAction } from 'vs/editor/common/modes/languageConfiguration';
import { IIndentConverter, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules';
import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils';
export class MoveLinesCommand implements ICommand {
@@ -64,7 +63,7 @@ export class MoveLinesCommand implements ICommand {
getLanguageIdAtPosition: (lineNumber: number, column: number) => {
return model.getLanguageIdAtPosition(lineNumber, column);
},
getLineContent: <(lineNumber: number) => string>null,
getLineContent: null as unknown as (lineNumber: number) => string,
};
if (s.startLineNumber === s.endLineNumber && model.getLineMaxColumn(s.startLineNumber) === 1) {
@@ -104,8 +103,8 @@ export class MoveLinesCommand implements ICommand {
// if s.startLineNumber - 1 matches onEnter rule, we still honor that.
if (movingLineMatchResult !== null) {
let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
let newSpaceCnt = movingLineMatchResult + IndentUtil.getSpaceCnt(oldIndentation, tabSize);
let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
let newSpaceCnt = movingLineMatchResult + indentUtils.getSpaceCnt(oldIndentation, tabSize);
let newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + this.trimLeft(movingLineText);
} else {
// no enter rule matches, let's check indentatin rules then.
@@ -120,10 +119,10 @@ export class MoveLinesCommand implements ICommand {
movingLineNumber, 1), s.startLineNumber, indentConverter);
if (indentOfMovingLine !== null) {
let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfMovingLine, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
let newSpaceCnt = indentUtils.getSpaceCnt(indentOfMovingLine, tabSize);
let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
let newIndentation = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + this.trimLeft(movingLineText);
}
}
@@ -156,8 +155,8 @@ export class MoveLinesCommand implements ICommand {
if (newIndentatOfMovingBlock !== null) {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
const newSpaceCnt = IndentUtil.getSpaceCnt(newIndentatOfMovingBlock, tabSize);
const oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
const newSpaceCnt = indentUtils.getSpaceCnt(newIndentatOfMovingBlock, tabSize);
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
const spaceCntOffset = newSpaceCnt - oldSpaceCnt;
@@ -200,8 +199,8 @@ export class MoveLinesCommand implements ICommand {
if (indentOfFirstLine !== null) {
// adjust the indentation of the moving block
let oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndent, tabSize);
let newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndent, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let spaceCntOffset = newSpaceCnt - oldSpaceCnt;
@@ -282,11 +281,11 @@ export class MoveLinesCommand implements ICommand {
let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(line));
let newIndentation = strings.getLeadingWhitespace(enterPrefix);
let indentMetadataOfMovelingLine = LanguageConfigurationRegistry.getIndentMetadata(model, line);
if (indentMetadataOfMovelingLine & IndentConsts.DECREASE_MASK) {
if (indentMetadataOfMovelingLine !== null && indentMetadataOfMovelingLine & IndentConsts.DECREASE_MASK) {
newIndentation = indentConverter.unshiftIndent(newIndentation);
}
let newSpaceCnt = IndentUtil.getSpaceCnt(newIndentation, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
let newSpaceCnt = indentUtils.getSpaceCnt(newIndentation, tabSize);
let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
return newSpaceCnt - oldSpaceCnt;
}
}
@@ -324,9 +323,9 @@ export class MoveLinesCommand implements ICommand {
for (let i = s.startLineNumber; i <= s.endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize);
let originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + offset;
let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces);
let newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces);
if (newIndent !== originalIndent) {
builder.addEditOperation(new Range(i, 1, i, originalIndent.length + 1), newIndent);

View File

@@ -2,13 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
export class SortLinesCommand implements editorCommon.ICommand {
@@ -34,7 +33,11 @@ export class SortLinesCommand implements editorCommon.ICommand {
return helper.getTrackedSelection(this.selectionId);
}
public static canRun(model: ITextModel, selection: Selection, descending: boolean): boolean {
public static canRun(model: ITextModel | null, selection: Selection, descending: boolean): boolean {
if (model === null) {
return false;
}
let data = getSortData(model, selection, descending);
if (!data) {
@@ -64,7 +67,7 @@ function getSortData(model: ITextModel, selection: Selection, descending: boolea
return null;
}
let linesToSort = [];
let linesToSort: string[] = [];
// Get the contents of the selection to be sorted.
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
@@ -92,7 +95,7 @@ function getSortData(model: ITextModel, selection: Selection, descending: boolea
/**
* Generate commands for sorting lines on a model.
*/
function sortLines(model: ITextModel, selection: Selection, descending: boolean): IIdentifiedSingleEditOperation {
function sortLines(model: ITextModel, selection: Selection, descending: boolean): IIdentifiedSingleEditOperation | null {
let data = getSortData(model, selection, descending);
if (!data) {

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { CopyLinesCommand } from 'vs/editor/contrib/linesOperations/copyLinesCommand';

View File

@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { DeleteLinesCommand } from 'vs/editor/contrib/linesOperations/deleteLinesCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';

View File

@@ -2,17 +2,15 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Selection } from 'vs/editor/common/core/selection';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { Handler } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { DeleteAllLeftAction, JoinLinesAction, TransposeAction, UpperCaseAction, LowerCaseAction, DeleteAllRightAction, InsertLineBeforeAction, InsertLineAfterAction, IndentLinesAction, SortLinesAscendingAction, SortLinesDescendingAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('Editor Contrib - Line Operations', () => {
@@ -447,7 +445,7 @@ suite('Editor Contrib - Line Operations', () => {
});
});
test('transpose', function () {
test('transpose', () => {
withTestCodeEditor(
[
'hello world',
@@ -774,7 +772,7 @@ suite('Editor Contrib - Line Operations', () => {
});
});
test('InsertLineBeforeAction', function () {
test('InsertLineBeforeAction', () => {
function testInsertLineBefore(lineNumber: number, column: number, callback: (model: ITextModel, cursor: Cursor) => void): void {
const TEXT = [
'First line',
@@ -881,4 +879,24 @@ suite('Editor Contrib - Line Operations', () => {
model.dispose();
});
test('issue #62112: Delete line does not work properly when multiple cursors are on line', () => {
const TEXT = [
'a',
'foo boo',
'too',
'c',
];
withTestCodeEditor(TEXT, {}, (editor, cursor) => {
editor.setSelections([
new Selection(2, 4, 2, 4),
new Selection(2, 8, 2, 8),
new Selection(3, 4, 3, 4),
]);
const deleteLinesAction = new DeleteLinesAction();
deleteLinesAction.run(null, editor);
assert.equal(editor.getValue(), 'a\nc');
});
});
});

View File

@@ -2,14 +2,13 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { IndentationRule } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
function testMoveLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, true, false), expectedLines, expectedSelection);

View File

@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/sortLinesCommand';

View File

@@ -3,18 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Range, IRange } from 'vs/editor/common/core/range';
import { URI } from 'vs/base/common/uri';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { ILink, LinkProvider, LinkProviderRegistry } from 'vs/editor/common/modes';
import { asWinJsPromise } from 'vs/base/common/async';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
export class Link implements ILink {
@@ -37,32 +33,32 @@ export class Link implements ILink {
return this._link.range;
}
get url(): string {
get url(): string | undefined {
return this._link.url;
}
resolve(): TPromise<URI> {
resolve(token: CancellationToken): Thenable<URI> {
if (this._link.url) {
try {
return TPromise.as(URI.parse(this._link.url));
return Promise.resolve(URI.parse(this._link.url));
} catch (e) {
return TPromise.wrapError<URI>(new Error('invalid'));
return Promise.reject(new Error('invalid'));
}
}
if (typeof this._provider.resolveLink === 'function') {
return asWinJsPromise(token => this._provider.resolveLink(this._link, token)).then(value => {
return Promise.resolve(this._provider.resolveLink(this._link, token)).then(value => {
this._link = value || this._link;
if (this._link.url) {
// recurse
return this.resolve();
return this.resolve(token);
}
return TPromise.wrapError<URI>(new Error('missing'));
return Promise.reject(new Error('missing'));
});
}
return TPromise.wrapError<URI>(new Error('missing'));
return Promise.reject(new Error('missing'));
}
}

View File

@@ -3,28 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./links';
import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as platform from 'vs/base/common/platform';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { LinkProviderRegistry } from 'vs/editor/common/modes';
import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { getLinks, Link } from 'vs/editor/contrib/links/getLinks';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { Position } from 'vs/editor/common/core/position';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { INotificationService } from 'vs/platform/notification/common/notification';
import * as async from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { LinkProviderRegistry } from 'vs/editor/common/modes';
import { ClickLinkGesture, ClickLinkKeyboardEvent, ClickLinkMouseEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture';
import { Link, getLinks } from 'vs/editor/contrib/links/getLinks';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
const HOVER_MESSAGE_GENERAL_META = new MarkdownString().appendText(
platform.isMacintosh
@@ -53,41 +52,49 @@ const HOVER_MESSAGE_COMMAND_ALT = new MarkdownString().appendText(
const decoration = {
meta: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_GENERAL_META
}),
metaActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_GENERAL_META
}),
alt: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_GENERAL_ALT
}),
altActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_GENERAL_ALT
}),
altCommand: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_COMMAND_ALT
}),
altCommandActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_COMMAND_ALT
}),
metaCommand: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link',
hoverMessage: HOVER_MESSAGE_COMMAND_META
}),
metaCommandActive: ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
collapseOnReplaceEdit: true,
inlineClassName: 'detected-link-active',
hoverMessage: HOVER_MESSAGE_COMMAND_META
}),
@@ -104,7 +111,7 @@ class LinkOccurrence {
}
private static _getOptions(link: Link, useMetaKey: boolean, isActive: boolean): ModelDecorationOptions {
if (/^command:/i.test(link.url)) {
if (link.url && /^command:/i.test(link.url)) {
if (useMetaKey) {
return (isActive ? decoration.metaCommandActive : decoration.metaCommand);
} else {
@@ -150,8 +157,8 @@ class LinkDetector implements editorCommon.IEditorContribution {
private enabled: boolean;
private listenersToRemove: IDisposable[];
private timeout: async.TimeoutTimer;
private computePromise: async.CancelablePromise<Link[]>;
private activeLinkDecorationId: string;
private computePromise: async.CancelablePromise<Link[]> | null;
private activeLinkDecorationId: string | null;
private openerService: IOpenerService;
private notificationService: INotificationService;
private currentOccurrences: { [decorationId: string]: LinkOccurrence; };
@@ -229,15 +236,17 @@ class LinkDetector implements editorCommon.IEditorContribution {
}
private async beginCompute(): Promise<void> {
if (!this.editor.getModel() || !this.enabled) {
if (!this.editor.hasModel() || !this.enabled) {
return;
}
if (!LinkProviderRegistry.has(this.editor.getModel())) {
const model = this.editor.getModel();
if (!LinkProviderRegistry.has(model)) {
return;
}
this.computePromise = async.createCancelablePromise(token => getLinks(this.editor.getModel(), token));
this.computePromise = async.createCancelablePromise(token => getLinks(model, token));
try {
const links = await this.computePromise;
this.updateDecorations(links);
@@ -276,7 +285,7 @@ class LinkDetector implements editorCommon.IEditorContribution {
}
}
private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void {
private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent, withKey: ClickLinkKeyboardEvent | null): void {
const useMetaKey = (this.editor.getConfiguration().multiCursorModifier === 'altKey');
if (this.isEnabled(mouseEvent, withKey)) {
this.cleanUpActiveLinkDecoration(); // always remove previous link decoration as their can only be one
@@ -325,7 +334,7 @@ class LinkDetector implements editorCommon.IEditorContribution {
const { link } = occurrence;
link.resolve().then(uri => {
link.resolve(CancellationToken.None).then(uri => {
// open the uri
return this.openerService.open(uri, { openToSide });
@@ -338,10 +347,13 @@ class LinkDetector implements editorCommon.IEditorContribution {
} else {
onUnexpectedError(err);
}
}).done(null, onUnexpectedError);
});
}
public getLinkOccurrence(position: Position): LinkOccurrence {
public getLinkOccurrence(position: Position | null): LinkOccurrence | null {
if (!this.editor.hasModel() || !position) {
return null;
}
const decorations = this.editor.getModel().getDecorationsInRange({
startLineNumber: position.lineNumber,
startColumn: position.column,
@@ -360,9 +372,9 @@ class LinkDetector implements editorCommon.IEditorContribution {
return null;
}
private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean {
return (
mouseEvent.target.type === MouseTargetType.CONTENT_TEXT
private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent | null): boolean {
return Boolean(
(mouseEvent.target.type === MouseTargetType.CONTENT_TEXT)
&& (mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey))
);
}
@@ -398,6 +410,9 @@ class OpenLinkAction extends EditorAction {
if (!linkDetector) {
return;
}
if (!editor.hasModel()) {
return;
}
let selections = editor.getSelections();
@@ -415,7 +430,7 @@ registerEditorContribution(LinkDetector);
registerEditorAction(OpenLinkAction);
registerThemingParticipant((theme, collector) => {
let activeLinkForeground = theme.getColor(editorActiveLinkForeground);
const activeLinkForeground = theme.getColor(editorActiveLinkForeground);
if (activeLinkForeground) {
collector.addRule(`.monaco-editor .detected-link-active { color: ${activeLinkForeground} !important; }`);
}

View File

@@ -3,20 +3,18 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TokenizationRegistry } from 'vs/editor/common/modes';
export interface IMarkdownRenderResult extends IDisposable {
element: HTMLElement;
@@ -30,22 +28,33 @@ export class MarkdownRenderer {
constructor(
private readonly _editor: ICodeEditor,
@IModeService private readonly _modeService: IModeService,
@optional(IOpenerService) private readonly _openerService: IOpenerService = NullOpenerService,
@optional(IOpenerService) private readonly _openerService: IOpenerService | null = NullOpenerService,
) {
}
private getOptions(disposeables: IDisposable[]): RenderOptions {
return {
codeBlockRenderer: (languageAlias, value): TPromise<string> => {
codeBlockRenderer: (languageAlias, value) => {
// In markdown,
// it is possible that we stumble upon language aliases (e.g.js instead of javascript)
// it is possible no alias is given in which case we fall back to the current editor lang
const modeId = languageAlias
? this._modeService.getModeIdForLanguageName(languageAlias)
: this._editor.getModel().getLanguageIdentifier().language;
let modeId: string | null = null;
if (languageAlias) {
modeId = this._modeService.getModeIdForLanguageName(languageAlias);
} else {
const model = this._editor.getModel();
if (model) {
modeId = model.getLanguageIdentifier().language;
}
}
return this._modeService.getOrCreateMode(modeId).then(_ => {
return tokenizeToString(value, modeId);
this._modeService.triggerMode(modeId || '');
return Promise.resolve(true).then(_ => {
const promise = TokenizationRegistry.getPromise(modeId || '');
if (promise) {
return promise.then(support => tokenizeToString(value, support));
}
return tokenizeToString(value, undefined);
}).then(code => {
return `<span style="font-family: ${this._editor.getConfiguration().fontInfo.fontFamily}">${code}</span>`;
});
@@ -53,7 +62,15 @@ export class MarkdownRenderer {
codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(),
actionHandler: {
callback: (content) => {
this._openerService.open(URI.parse(content)).then(void 0, onUnexpectedError);
let uri: URI | undefined;
try {
uri = URI.parse(content);
} catch {
// ignore
}
if (uri && this._openerService) {
this._openerService.open(uri).catch(onUnexpectedError);
}
},
disposeables
}

View File

@@ -3,11 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./messageController';
import * as nls from 'vs/nls';
import { setDisposableTimeout } from 'vs/base/common/async';
import { TimeoutTimer } from 'vs/base/common/async';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { alert } from 'vs/base/browser/ui/aria/aria';
@@ -18,7 +16,7 @@ import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPosit
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IPosition } from 'vs/editor/common/core/position';
import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { inputValidationInfoBorder, inputValidationInfoBackground } from 'vs/platform/theme/common/colorRegistry';
import { inputValidationInfoBorder, inputValidationInfoBackground, inputValidationInfoForeground } from 'vs/platform/theme/common/colorRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export class MessageController extends Disposable implements editorCommon.IEditorContribution {
@@ -75,7 +73,7 @@ export class MessageController extends Disposable implements editorCommon.IEdito
this._messageListeners.push(this._editor.onDidChangeModel(() => this.closeMessage()));
// close after 3s
this._messageListeners.push(setDisposableTimeout(() => this.closeMessage(), 3000));
this._messageListeners.push(new TimeoutTimer(() => this.closeMessage(), 3000));
// close on mouse move
let bounds: Range;
@@ -102,7 +100,9 @@ export class MessageController extends Disposable implements editorCommon.IEdito
}
private _onDidAttemptReadOnlyEdit(): void {
this.showMessage(nls.localize('editor.readonly', "Cannot edit in read-only editor"), this._editor.getPosition());
if (this._editor.hasModel()) {
this.showMessage(nls.localize('editor.readonly', "Cannot edit in read-only editor"), this._editor.getPosition());
}
}
}
@@ -130,7 +130,7 @@ class MessageWidget implements IContentWidget {
private _domNode: HTMLDivElement;
static fadeOut(messageWidget: MessageWidget): IDisposable {
let handle: number;
let handle: any;
const dispose = () => {
messageWidget.dispose();
clearTimeout(handle);
@@ -184,14 +184,18 @@ class MessageWidget implements IContentWidget {
registerEditorContribution(MessageController);
registerThemingParticipant((theme, collector) => {
let border = theme.getColor(inputValidationInfoBorder);
const border = theme.getColor(inputValidationInfoBorder);
if (border) {
let borderWidth = theme.type === HIGH_CONTRAST ? 2 : 1;
collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: ${border}; }`);
collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { border: ${borderWidth}px solid ${border}; }`);
}
let background = theme.getColor(inputValidationInfoBackground);
const background = theme.getColor(inputValidationInfoBackground);
if (background) {
collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { background-color: ${background}; }`);
}
const foreground = theme.getColor(inputValidationInfoForeground);
if (foreground) {
collector.addRule(`.monaco-editor .monaco-editor-overlaymessage .message { color: ${foreground}; }`);
}
});

View File

@@ -2,31 +2,30 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon';
import { FindMatch, TrackedRangeStickiness, OverviewRulerLane, ITextModel } from 'vs/editor/common/model';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { RevealTarget } from 'vs/editor/common/controller/cursorCommon';
import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands';
import { RevealTarget } from 'vs/editor/common/controller/cursorCommon';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { Constants } from 'vs/editor/common/core/uint';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { FindMatch, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { DocumentHighlightProviderRegistry } from 'vs/editor/common/modes';
import { CommonFindController } from 'vs/editor/contrib/find/findController';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { FindOptionOverride, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
import { MenuId } from 'vs/platform/actions/common/actions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { INewFindReplaceState, FindOptionOverride } from 'vs/editor/contrib/find/findState';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MenuId } from 'vs/platform/actions/common/actions';
export class InsertCursorAbove extends EditorAction {
@@ -166,6 +165,57 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction {
}
}
class InsertCursorAtEndOfLineSelected extends EditorAction {
constructor() {
super({
id: 'editor.action.addCursorsToBottom',
label: nls.localize('mutlicursor.addCursorsToBottom', "Add Cursors To Bottom"),
alias: 'Add Cursors To Bottom',
precondition: null
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const selections = editor.getSelections();
const lineCount = editor.getModel().getLineCount();
let newSelections = [];
for (let i = selections[0].startLineNumber; i <= lineCount; i++) {
newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn));
}
if (newSelections.length > 0) {
editor.setSelections(newSelections);
}
}
}
class InsertCursorAtTopOfLineSelected extends EditorAction {
constructor() {
super({
id: 'editor.action.addCursorsToTop',
label: nls.localize('mutlicursor.addCursorsToTop', "Add Cursors To Top"),
alias: 'Add Cursors To Top',
precondition: null
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const selections = editor.getSelections();
let newSelections = [];
for (let i = selections[0].startLineNumber; i >= 1; i--) {
newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn));
}
if (newSelections.length > 0) {
editor.setSelections(newSelections);
}
}
}
export class MultiCursorSessionResult {
constructor(
public readonly selections: Selection[],
@@ -207,7 +257,7 @@ export class MultiCursorSession {
const s = editor.getSelection();
let searchText: string;
let currentMatch: Selection = null;
let currentMatch: Selection | null = null;
if (s.isEmpty()) {
// selection is empty => expand to current word
@@ -477,7 +527,7 @@ export class MultiCursorSelectionController extends Disposable implements IEdito
}
public selectAll(findController: CommonFindController): void {
let matches: FindMatch[] = null;
let matches: FindMatch[] | null = null;
const findState = findController.getState();
@@ -663,13 +713,11 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction {
}
class SelectionHighlighterState {
public readonly lastWordUnderCursor: Selection;
public readonly searchText: string;
public readonly matchCase: boolean;
public readonly wordSeparators: string;
constructor(lastWordUnderCursor: Selection, searchText: string, matchCase: boolean, wordSeparators: string) {
this.lastWordUnderCursor = lastWordUnderCursor;
constructor(searchText: string, matchCase: boolean, wordSeparators: string) {
this.searchText = searchText;
this.matchCase = matchCase;
this.wordSeparators = wordSeparators;
@@ -723,7 +771,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
if (e.selection.isEmpty()) {
if (e.reason === CursorChangeReason.Explicit) {
if (this.state && (!this.state.lastWordUnderCursor || !this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) {
if (this.state) {
// no longer valid
this._setState(null);
}
@@ -791,21 +839,10 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
return null;
}
let lastWordUnderCursor: Selection = null;
const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model);
if (r.currentMatch) {
// This is an empty selection
if (hasFindOccurrences) {
// Do not interfere with semantic word highlighting in the no selection case
return null;
}
const config = editor.getConfiguration();
if (!config.contribInfo.occurrencesHighlight) {
return null;
}
lastWordUnderCursor = r.currentMatch;
// Do not interfere with semantic word highlighting in the no selection case
return null;
}
if (/^[ \t]+$/.test(r.searchText)) {
// whitespace only selection
@@ -837,7 +874,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
}
}
return new SelectionHighlighterState(lastWordUnderCursor, r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null);
return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null);
}
private _setState(state: SelectionHighlighterState): void {
@@ -910,7 +947,6 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
className: 'selectionHighlight',
overviewRuler: {
color: themeColorFromId(overviewRulerSelectionHighlightForeground),
darkColor: themeColorFromId(overviewRulerSelectionHighlightForeground),
position: OverviewRulerLane.Center
}
});
@@ -958,3 +994,5 @@ registerEditorAction(MoveSelectionToNextFindMatchAction);
registerEditorAction(MoveSelectionToPreviousFindMatchAction);
registerEditorAction(SelectHighlightsAction);
registerEditorAction(CompatChangeAll);
registerEditorAction(InsertCursorAtEndOfLineSelected);
registerEditorAction(InsertCursorAtTopOfLineSelected);

View File

@@ -2,18 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { withTestCodeEditor, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { Selection } from 'vs/editor/common/core/selection';
import { Event } from 'vs/base/common/event';
import { Range } from 'vs/editor/common/core/range';
import { InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction, AddSelectionToNextFindMatchAction } from 'vs/editor/contrib/multicursor/multicursor';
import { Selection } from 'vs/editor/common/core/selection';
import { Handler } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence } from 'vs/editor/common/model';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { CommonFindController } from 'vs/editor/contrib/find/findController';
import { AddSelectionToNextFindMatchAction, InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction } from 'vs/editor/contrib/multicursor/multicursor';
import { TestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IStorageService } from 'vs/platform/storage/common/storage';
suite('Multicursor', () => {
@@ -61,9 +60,14 @@ suite('Multicursor selection', () => {
let queryState: { [key: string]: any; } = {};
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, {
_serviceBrand: undefined,
onDidChangeStorage: Event.None,
onWillSaveState: Event.None,
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
getInteger: (key: string) => undefined,
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
remove: (key) => void 0
} as IStorageService);
test('issue #8817: Cursor position changes when you cancel multicursor', () => {

View File

@@ -22,9 +22,6 @@
}
.monaco-editor .parameter-hints-widget.visible {
-webkit-transition: left .05s ease-in-out;
-moz-transition: left .05s ease-in-out;
-o-transition: left .05s ease-in-out;
transition: left .05s ease-in-out;
}
@@ -107,10 +104,6 @@
display: block;
}
.monaco-editor .parameter-hints-widget .signature .parameter {
display: inline-block;
}
.monaco-editor .parameter-hints-widget .signature .parameter.active {
font-weight: bold;
text-decoration: underline;
@@ -131,4 +124,4 @@
.monaco-editor.hc-black .parameter-hints-widget .button.next,
.monaco-editor.vs-dark .parameter-hints-widget .button.next {
background-image: url('arrow-down-dark.svg');
}
}

Some files were not shown because too many files have changed in this diff Show More