Initial VS Code 1.19 source merge (#571)

* Initial 1.19 xcopy

* Fix yarn build

* Fix numerous build breaks

* Next batch of build break fixes

* More build break fixes

* Runtime breaks

* Additional post merge fixes

* Fix windows setup file

* Fix test failures.

* Update license header blocks to refer to source eula
This commit is contained in:
Karl Burtram
2018-01-28 23:37:17 -08:00
committed by GitHub
parent 9a1ac20710
commit 251ae01c3e
8009 changed files with 93378 additions and 35634 deletions

View File

@@ -5,6 +5,7 @@
'use strict';
import 'vs/css!./bracketMatching';
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -13,13 +14,13 @@ 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, commonEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
class SelectBracketAction extends EditorAction {
constructor() {
super({
@@ -34,7 +35,7 @@ class SelectBracketAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = BracketMatchingController.get(editor);
if (!controller) {
return;
@@ -55,15 +56,14 @@ class BracketsData {
}
}
@commonEditorContribution
export class BracketMatchingController extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.bracketMatchingController';
private static readonly ID = 'editor.contrib.bracketMatchingController';
public static get(editor: editorCommon.ICommonCodeEditor): BracketMatchingController {
public static get(editor: ICodeEditor): BracketMatchingController {
return editor.getContribution<BracketMatchingController>(BracketMatchingController.ID);
}
private readonly _editor: editorCommon.ICommonCodeEditor;
private readonly _editor: ICodeEditor;
private _lastBracketsData: BracketsData[];
private _lastVersionId: number;
@@ -72,7 +72,7 @@ export class BracketMatchingController extends Disposable implements editorCommo
private _matchBrackets: boolean;
constructor(
editor: editorCommon.ICommonCodeEditor
editor: ICodeEditor
) {
super();
this._editor = editor;
@@ -148,7 +148,7 @@ export class BracketMatchingController extends Disposable implements editorCommo
this._editor.revealRange(newSelections[0]);
}
private static _DECORATION_OPTIONS = ModelDecorationOptions.register({
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'bracket-match'
});
@@ -226,6 +226,8 @@ export class BracketMatchingController extends Disposable implements editorCommo
}
}
registerEditorContribution(BracketMatchingController);
registerEditorAction(SelectBracketAction);
registerThemingParticipant((theme, collector) => {
let bracketMatchBackground = theme.getColor(editorBracketMatchBackground);
if (bracketMatchBackground) {

View File

@@ -5,18 +5,18 @@
'use strict';
import * as assert from 'assert';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { Position } from 'vs/editor/common/core/position';
import { Model } from 'vs/editor/common/model/model';
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 { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/common/bracketMatching';
import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/bracketMatching';
suite('bracket matching', () => {
class BracketMode extends MockMode {
private static _id = new LanguageIdentifier('bracketMode', 3);
private static readonly _id = new LanguageIdentifier('bracketMode', 3);
constructor() {
super(BracketMode._id);
@@ -34,7 +34,7 @@ suite('bracket matching', () => {
let mode = new BracketMode();
let model = Model.createFromString('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier());
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController);
// start on closing bracket
@@ -66,7 +66,7 @@ suite('bracket matching', () => {
let mode = new BracketMode();
let model = Model.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier());
withMockCodeEditor(null, { model: model }, (editor, cursor) => {
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
let bracketMatchingController = editor.registerAndInstantiateContribution<BracketMatchingController>(BracketMatchingController);
// start position between brackets

View File

@@ -5,10 +5,11 @@
'use strict';
import * as nls from 'vs/nls';
import { ICommand, ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IActionOptions, editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions';
import { IActionOptions, registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { MoveCaretCommand } from './moveCaretCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
class MoveCaretAction extends EditorAction {
@@ -20,7 +21,7 @@ class MoveCaretAction extends EditorAction {
this.left = left;
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
var commands: ICommand[] = [];
var selections = editor.getSelections();
@@ -35,7 +36,6 @@ class MoveCaretAction extends EditorAction {
}
}
@editorAction
class MoveCaretLeftAction extends MoveCaretAction {
constructor() {
super(true, {
@@ -47,7 +47,6 @@ class MoveCaretLeftAction extends MoveCaretAction {
}
}
@editorAction
class MoveCaretRightAction extends MoveCaretAction {
constructor() {
super(false, {
@@ -58,3 +57,6 @@ class MoveCaretRightAction extends MoveCaretAction {
});
}
}
registerEditorAction(MoveCaretLeftAction);
registerEditorAction(MoveCaretRightAction);

View File

@@ -5,8 +5,8 @@
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/common/moveCaretCommand';
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
import { MoveCaretCommand } from 'vs/editor/contrib/caretOperations/moveCaretCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
function testMoveCaretLeftCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
@@ -67,4 +67,4 @@ suite('Editor Contrib - Move Caret Command', () => {
new Selection(1, 5, 1, 7)
);
});
});
});

View File

@@ -7,12 +7,12 @@
import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Range } from 'vs/editor/common/core/range';
import { ICommand, ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@editorAction
class TransposeLettersAction extends EditorAction {
constructor() {
@@ -31,7 +31,7 @@ class TransposeLettersAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let model = editor.getModel();
let commands: ICommand[] = [];
let selections = editor.getSelections();
@@ -69,3 +69,5 @@ class TransposeLettersAction extends EditorAction {
}
}
}
registerEditorAction(TransposeLettersAction);

View File

@@ -11,11 +11,11 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as browser from 'vs/base/browser/browser';
import * as platform from 'vs/base/common/platform';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { editorAction, IActionOptions, EditorAction, ICommandKeybindingsOptions } from 'vs/editor/common/editorCommonExtensions';
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';
const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
@@ -30,13 +30,6 @@ const supportsPaste = (platform.isNative || (!browser.isChrome && document.query
type ExecCommand = 'cut' | 'copy' | 'paste';
function conditionalEditorAction(condition: boolean) {
if (!condition) {
return () => { };
}
return editorAction;
}
abstract class ExecCommandAction extends EditorAction {
private browserCommand: ExecCommand;
@@ -57,13 +50,12 @@ abstract class ExecCommandAction extends EditorAction {
document.execCommand(this.browserCommand);
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
editor.focus();
document.execCommand(this.browserCommand);
}
}
@conditionalEditorAction(supportsCut)
class ExecCommandCutAction extends ExecCommandAction {
constructor() {
@@ -90,7 +82,7 @@ class ExecCommandCutAction extends ExecCommandAction {
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard;
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
@@ -101,7 +93,6 @@ class ExecCommandCutAction extends ExecCommandAction {
}
}
@conditionalEditorAction(supportsCopy)
class ExecCommandCopyAction extends ExecCommandAction {
constructor() {
@@ -129,7 +120,7 @@ class ExecCommandCopyAction extends ExecCommandAction {
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard;
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
@@ -140,7 +131,6 @@ class ExecCommandCopyAction extends ExecCommandAction {
}
}
@conditionalEditorAction(supportsPaste)
class ExecCommandPasteAction extends ExecCommandAction {
constructor() {
@@ -169,7 +159,6 @@ class ExecCommandPasteAction extends ExecCommandAction {
}
}
@conditionalEditorAction(supportsCopyWithSyntaxHighlighting)
class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
constructor() {
@@ -185,7 +174,7 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const emptySelectionClipboard = editor.getConfiguration().emptySelectionClipboard;
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
@@ -197,3 +186,16 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
CopyOptions.forceCopyWithSyntaxHighlighting = false;
}
}
if (supportsCut) {
registerEditorAction(ExecCommandCutAction);
}
if (supportsCopy) {
registerEditorAction(ExecCommandCopyAction);
}
if (supportsPaste) {
registerEditorAction(ExecCommandPasteAction);
}
if (supportsCopyWithSyntaxHighlighting) {
registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction);
}

View File

@@ -10,7 +10,7 @@ import { mergeSort } from 'vs/base/common/arrays';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModel } from 'vs/editor/common/editorCommon';
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
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 { asWinJsPromise } from 'vs/base/common/async';
@@ -56,7 +56,7 @@ export function getCodeLensData(model: IModel): TPromise<ICodeLensData[]> {
});
}
CommonEditorRegistry.registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) {
registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) {
const { resource } = args;
if (!(resource instanceof URI)) {

View File

@@ -14,12 +14,11 @@ import { IMessageService } from 'vs/platform/message/common/message';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeLensData, getCodeLensData } from './codelens';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/browser/codelensWidget';
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
@editorContribution
export class CodeLensContribution implements editorCommon.IEditorContribution {
private static ID: string = 'css.editor.codeLens';
@@ -304,3 +303,5 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
});
}
}
registerEditorContribution(CodeLensContribution);

View File

@@ -65,7 +65,6 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
private readonly _disposables: IDisposable[] = [];
private readonly _editor: editorBrowser.ICodeEditor;
private _symbolRange: Range;
private _widgetPosition: editorBrowser.IContentWidgetPosition;
private _commands: { [id: string]: Command } = Object.create(null);
@@ -107,7 +106,6 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
dispose(): void {
dispose(this._disposables);
this._symbolRange = null;
}
private _updateHeight(): void {
@@ -159,8 +157,6 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
}
setSymbolRange(range: Range): void {
this._symbolRange = range;
const lineNumber = range.startLineNumber;
const column = this._editor.getModel().getLineFirstNonWhitespaceColumn(lineNumber);
this._widgetPosition = {

View File

@@ -7,19 +7,18 @@ import { RGBA } from 'vs/base/common/color';
import { hash } from 'vs/base/common/hash';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICommonCodeEditor, IEditorContribution } from 'vs/editor/common/editorCommon';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
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 { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { getColors, IColorData } from 'vs/editor/contrib/colorPicker/common/color';
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';
const MAX_DECORATORS = 500;
@editorContribution
export class ColorDetector implements IEditorContribution {
private static ID: string = 'editor.contrib.colorDetector';
@@ -74,7 +73,7 @@ export class ColorDetector implements IEditorContribution {
}
const languageId = model.getLanguageIdentifier();
// handle deprecated settings. [languageId].colorDecorators.enable
let deprecatedConfig = this._configurationService.getConfiguration(languageId.language);
let deprecatedConfig = this._configurationService.getValue(languageId.language);
if (deprecatedConfig) {
let colorDecorators = deprecatedConfig['colorDecorators']; // deprecatedConfig.valueOf('.colorDecorators.enable');
if (colorDecorators && colorDecorators['enable'] !== undefined && !colorDecorators['enable']) {
@@ -89,7 +88,7 @@ export class ColorDetector implements IEditorContribution {
return ColorDetector.ID;
}
static get(editor: ICommonCodeEditor): ColorDetector {
static get(editor: ICodeEditor): ColorDetector {
return editor.getContribution<ColorDetector>(this.ID);
}
@@ -234,3 +233,5 @@ export class ColorDetector implements IEditorContribution {
return this._colorDatas.get(decorations[0].id);
}
}
registerEditorContribution(ColorDetector);

View File

@@ -8,7 +8,7 @@ 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/browser/colorPickerModel';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
import { Disposable } from 'vs/base/common/lifecycle';
import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
import { Color, RGBA, HSVA } from 'vs/base/common/color';
@@ -67,7 +67,7 @@ export class ColorPickerBody extends Disposable {
private hueStrip: Strip;
private opacityStrip: Strip;
constructor(private container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) {
constructor(container: HTMLElement, private model: ColorPickerModel, private pixelRatio: number) {
super();
this.domNode = $('.colorpicker-body');
@@ -328,7 +328,7 @@ class HueStrip extends Strip {
export class ColorPickerWidget extends Widget {
private static ID = 'editor.contrib.colorPickerWidget';
private static readonly ID = 'editor.contrib.colorPickerWidget';
body: ColorPickerBody;
@@ -354,4 +354,4 @@ export class ColorPickerWidget extends Widget {
layout(): void {
this.body.layout();
}
}
}

View File

Before

Width:  |  Height:  |  Size: 173 B

After

Width:  |  Height:  |  Size: 173 B

View File

@@ -6,11 +6,12 @@
import * as nls from 'vs/nls';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { ICommand, ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { ICommand } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, IActionOptions, EditorAction, ServicesAccessor } from 'vs/editor/common/editorCommonExtensions';
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';
abstract class CommentLineAction extends EditorAction {
@@ -21,7 +22,7 @@ abstract class CommentLineAction extends EditorAction {
this._type = type;
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let model = editor.getModel();
if (!model) {
return;
@@ -42,7 +43,6 @@ abstract class CommentLineAction extends EditorAction {
}
@editorAction
class ToggleCommentLineAction extends CommentLineAction {
constructor() {
super(Type.Toggle, {
@@ -58,7 +58,6 @@ class ToggleCommentLineAction extends CommentLineAction {
}
}
@editorAction
class AddLineCommentAction extends CommentLineAction {
constructor() {
super(Type.ForceAdd, {
@@ -74,7 +73,6 @@ class AddLineCommentAction extends CommentLineAction {
}
}
@editorAction
class RemoveLineCommentAction extends CommentLineAction {
constructor() {
super(Type.ForceRemove, {
@@ -90,7 +88,6 @@ class RemoveLineCommentAction extends CommentLineAction {
}
}
@editorAction
class BlockCommentAction extends EditorAction {
constructor() {
@@ -107,7 +104,7 @@ class BlockCommentAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
var commands: ICommand[] = [];
var selections = editor.getSelections();
@@ -120,3 +117,8 @@ class BlockCommentAction extends EditorAction {
editor.pushUndoStop();
}
}
registerEditorAction(ToggleCommentLineAction);
registerEditorAction(AddLineCommentAction);
registerEditorAction(RemoveLineCommentAction);
registerEditorAction(BlockCommentAction);

View File

@@ -5,8 +5,8 @@
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/common/blockCommentCommand';
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
import { CommentMode } from 'vs/editor/test/common/commentMode';
function testBlockCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {

View File

@@ -6,8 +6,8 @@
import * as assert from 'assert';
import { Selection } from 'vs/editor/common/core/selection';
import { ILinePreflightData, IPreflightData, ISimpleModel, LineCommentCommand, Type } from 'vs/editor/contrib/comment/common/lineCommentCommand';
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
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';

View File

@@ -16,23 +16,21 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie
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 { ICommonCodeEditor, IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
export interface IPosition {
x: number;
y: number;
}
@editorContribution
export class ContextMenuController implements IEditorContribution {
private static ID = 'editor.contrib.contextmenu';
private static readonly ID = 'editor.contrib.contextmenu';
public static get(editor: ICommonCodeEditor): ContextMenuController {
public static get(editor: ICodeEditor): ContextMenuController {
return editor.getContribution<ContextMenuController>(ContextMenuController.ID);
}
@@ -217,7 +215,6 @@ export class ContextMenuController implements IEditorContribution {
}
}
@editorAction
class ShowContextMenu extends EditorAction {
constructor() {
@@ -233,8 +230,11 @@ class ShowContextMenu extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let contribution = ContextMenuController.get(editor);
contribution.showContextMenu();
}
}
registerEditorContribution(ContextMenuController);
registerEditorAction(ShowContextMenu);

View File

@@ -5,13 +5,12 @@
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { editorCommand, ServicesAccessor, EditorCommand } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorCommand, ServicesAccessor, EditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { ICommonCodeEditor, IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
class CursorState {
readonly selections: Selection[];
@@ -35,12 +34,11 @@ class CursorState {
}
}
@editorContribution
export class CursorUndoController extends Disposable implements IEditorContribution {
private static ID = 'editor.contrib.cursorUndoController';
private static readonly ID = 'editor.contrib.cursorUndoController';
public static get(editor: ICommonCodeEditor): CursorUndoController {
public static get(editor: ICodeEditor): CursorUndoController {
return editor.getContribution<CursorUndoController>(CursorUndoController.ID);
}
@@ -110,7 +108,6 @@ export class CursorUndoController extends Disposable implements IEditorContribut
}
}
@editorCommand
export class CursorUndo extends EditorCommand {
constructor() {
super({
@@ -123,7 +120,10 @@ export class CursorUndo extends EditorCommand {
});
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: any): void {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
CursorUndoController.get(editor).cursorUndo();
}
}
registerEditorContribution(CursorUndoController);
registerEditorCommand(new CursorUndo());

View File

@@ -11,18 +11,17 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isMacintosh } from 'vs/base/common/platform';
import { KeyCode } from 'vs/base/common/keyCodes';
import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import * as editorCommon from 'vs/editor/common/editorCommon';
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 { DragAndDropCommand } from '../common/dragAndDropCommand';
import { DragAndDropCommand } from 'vs/editor/contrib/dnd/dragAndDropCommand';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
@editorContribution
export class DragAndDropController implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.dragAndDrop';
private static readonly ID = 'editor.contrib.dragAndDrop';
private _editor: ICodeEditor;
private _toUnhook: IDisposable[];
@@ -33,7 +32,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
static TRIGGER_MODIFIER = isMacintosh ? 'altKey' : 'ctrlKey';
static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl;
static get(editor: editorCommon.ICommonCodeEditor): DragAndDropController {
static get(editor: ICodeEditor): DragAndDropController {
return editor.getContribution<DragAndDropController>(DragAndDropController.ID);
}
@@ -130,14 +129,20 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column);
if (this._dragSelection === null) {
let 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);
if (mouseEvent.event.shiftKey) {
let primarySelection = this._editor.getSelection();
let { startLineNumber, startColumn } = primarySelection;
this._editor.setSelections([new Selection(startLineNumber, startColumn, newCursorPosition.lineNumber, newCursorPosition.column)]);
} else {
let 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);
}
} else if (!this._dragSelection.containsPosition(newCursorPosition) ||
(
(
@@ -162,7 +167,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
this._mouseDown = false;
}
private static _DECORATION_OPTIONS = ModelDecorationOptions.register({
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
className: 'dnd-target'
});
@@ -207,4 +212,6 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
this._modiferPressed = false;
this._toUnhook = dispose(this._toUnhook);
}
}
}
registerEditorContribution(DragAndDropController);

View File

@@ -1,55 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { FindWidget, IFindController } from 'vs/editor/contrib/find/browser/findWidget';
import { FindOptionsWidget } from 'vs/editor/contrib/find/browser/findOptionsWidget';
import { CommonFindController, FindStartFocusAction, IFindStartOptions } from 'vs/editor/contrib/find/common/findController';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
@editorContribution
export class FindController extends CommonFindController implements IFindController {
private _widget: FindWidget;
private _findOptionsWidget: FindOptionsWidget;
constructor(
editor: ICodeEditor,
@IContextViewService contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService,
@IKeybindingService keybindingService: IKeybindingService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService
) {
super(editor, contextKeyService, storageService);
this._widget = this._register(new FindWidget(editor, this, this._state, contextViewService, keybindingService, contextKeyService, themeService));
this._findOptionsWidget = this._register(new FindOptionsWidget(editor, this._state, keybindingService, themeService));
}
protected _start(opts: IFindStartOptions): void {
super._start(opts);
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
this._widget.focusReplaceInput();
} else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) {
this._widget.focusFindInput();
}
}
public highlightFindOptions(): void {
if (this._state.isRevealed) {
this._widget.highlightFindOptions();
} else {
this._findOptionsWidget.highlightFindOptions();
}
}
}

View File

@@ -1,25 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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';
export function getSelectionSearchString(editor: editorCommon.ICommonCodeEditor): string {
let selection = editor.getSelection();
// if selection spans multiple lines, default search string to empty
if (selection.startLineNumber === selection.endLineNumber) {
if (selection.isEmpty()) {
let wordAtPosition = editor.getModel().getWordAtPosition(selection.getStartPosition());
if (wordAtPosition) {
return wordAtPosition.word;
}
} else {
return editor.getModel().getValueInRange(selection);
}
}
return null;
}

View File

@@ -8,16 +8,42 @@ import * as nls from 'vs/nls';
import { HistoryNavigator } from 'vs/base/common/history';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { ContextKeyExpr, RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, 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 { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding } from 'vs/editor/contrib/find/common/findModel';
import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/common/findState';
import { getSelectionSearchString } from 'vs/editor/contrib/find/common/find';
import { registerEditorContribution, registerEditorAction, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding, CONTEXT_FIND_WIDGET_VISIBLE, CONTEXT_FIND_INPUT_FOCUSED } 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 { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { optional } from 'vs/platform/instantiation/common/instantiation';
export function getSelectionSearchString(editor: ICodeEditor): string {
let selection = editor.getSelection();
// if selection spans multiple lines, default search string to empty
if (selection.startLineNumber === selection.endLineNumber) {
if (selection.isEmpty()) {
let wordAtPosition = editor.getModel().getWordAtPosition(selection.getStartPosition());
if (wordAtPosition) {
return wordAtPosition.word;
}
} else {
return editor.getModel().getValueInRange(selection);
}
}
return null;
}
export const enum FindStartFocusAction {
NoFocusChange,
@@ -28,42 +54,45 @@ export const enum FindStartFocusAction {
export interface IFindStartOptions {
forceRevealReplace: boolean;
seedSearchStringFromSelection: boolean;
seedSearchStringFromGlobalClipboard: boolean;
shouldFocus: FindStartFocusAction;
shouldAnimate: boolean;
}
export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('findWidgetVisible', false);
export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated();
// Keep ContextKey use of 'Focussed' to not break when clauses
export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey<boolean>('findInputFocussed', false);
export class CommonFindController extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.findController';
private static readonly ID = 'editor.contrib.findController';
private _editor: editorCommon.ICommonCodeEditor;
protected _editor: ICodeEditor;
private _findWidgetVisible: IContextKey<boolean>;
protected _state: FindReplaceState;
private _currentHistoryNavigator: HistoryNavigator<string>;
protected _updateHistoryDelayer: Delayer<void>;
private _model: FindModelBoundToEditorModel;
private _storageService: IStorageService;
private _clipboardService: IClipboardService;
public static get(editor: editorCommon.ICommonCodeEditor): CommonFindController {
public static get(editor: ICodeEditor): CommonFindController {
return editor.getContribution<CommonFindController>(CommonFindController.ID);
}
constructor(editor: editorCommon.ICommonCodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IStorageService storageService: IStorageService) {
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService
) {
super();
this._editor = editor;
this._findWidgetVisible = CONTEXT_FIND_WIDGET_VISIBLE.bindTo(contextKeyService);
this._storageService = storageService;
this._clipboardService = clipboardService;
this._updateHistoryDelayer = new Delayer<void>(500);
this._currentHistoryNavigator = new HistoryNavigator<string>();
this._state = this._register(new FindReplaceState());
this.loadQueryState();
this._register(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
this._model = null;
@@ -82,7 +111,8 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
if (shouldRestartFind) {
this._start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromSelection: false && this._editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false,
});
@@ -120,17 +150,20 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
this.disposeModel();
}
}
if (e.searchString) {
this.setGlobalBufferTerm(this._state.searchString);
}
}
private saveQueryState(e: FindReplaceStateChangedEvent) {
if (e.isRegex && typeof this._state.isRegex !== 'undefined') {
this._storageService.store('editor.isRegex', this._state.isRegex, StorageScope.WORKSPACE);
if (e.isRegex) {
this._storageService.store('editor.isRegex', this._state.actualIsRegex, StorageScope.WORKSPACE);
}
if (e.wholeWord && typeof this._state.wholeWord !== 'undefined') {
this._storageService.store('editor.wholeWord', this._state.wholeWord, StorageScope.WORKSPACE);
if (e.wholeWord) {
this._storageService.store('editor.wholeWord', this._state.actualWholeWord, StorageScope.WORKSPACE);
}
if (e.matchCase && typeof this._state.matchCase !== 'undefined') {
this._storageService.store('editor.matchCase', this._state.matchCase, StorageScope.WORKSPACE);
if (e.matchCase) {
this._storageService.store('editor.matchCase', this._state.actualMatchCase, StorageScope.WORKSPACE);
}
}
@@ -214,8 +247,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
isRevealed: true
};
// Consider editor selection and overwrite the state with it
if (opts.seedSearchStringFromSelection && this._editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection) {
if (opts.seedSearchStringFromSelection) {
let selectionSearchString = getSelectionSearchString(this._editor);
if (selectionSearchString) {
if (this._state.isRegex) {
@@ -226,6 +258,13 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
}
}
if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) {
let selectionSearchString = this.getGlobalBufferTerm();
if (selectionSearchString) {
stateChanges.searchString = selectionSearchString;
}
}
// Overwrite isReplaceRevealed
if (opts.forceRevealReplace) {
stateChanges.isReplaceRevealed = true;
@@ -301,9 +340,69 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
}
return true;
}
public getGlobalBufferTerm(): string {
if (this._editor.getConfiguration().contribInfo.find.globalFindClipboard && this._clipboardService) {
return this._clipboardService.readFindText();
}
return '';
}
public setGlobalBufferTerm(text: string) {
if (this._editor.getConfiguration().contribInfo.find.globalFindClipboard && this._clipboardService) {
this._clipboardService.writeFindText(text);
}
}
}
export class FindController extends CommonFindController implements IFindController {
private _widget: FindWidget;
private _findOptionsWidget: FindOptionsWidget;
constructor(
editor: ICodeEditor,
@IContextViewService private _contextViewService: IContextViewService,
@IContextKeyService private _contextKeyService: IContextKeyService,
@IKeybindingService private _keybindingService: IKeybindingService,
@IThemeService private _themeService: IThemeService,
@IStorageService storageService: IStorageService,
@optional(IClipboardService) clipboardService: IClipboardService
) {
super(editor, _contextKeyService, storageService, clipboardService);
}
protected _start(opts: IFindStartOptions): void {
if (!this._widget) {
this._createFindWidget();
}
super._start(opts);
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
this._widget.focusReplaceInput();
} else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) {
this._widget.focusFindInput();
}
}
public highlightFindOptions(): void {
if (!this._widget) {
this._createFindWidget();
}
if (this._state.isRevealed) {
this._widget.highlightFindOptions();
} else {
this._findOptionsWidget.highlightFindOptions();
}
}
private _createFindWidget() {
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService));
this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService));
}
}
@editorAction
export class StartFindAction extends EditorAction {
constructor() {
@@ -314,21 +413,18 @@ export class StartFindAction extends EditorAction {
precondition: null,
kbOpts: {
kbExpr: null,
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E]
}
primary: KeyMod.CtrlCmd | KeyCode.KEY_F
}
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = CommonFindController.get(editor);
if (controller) {
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: true,
seedSearchStringFromSelection: editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getConfiguration().contribInfo.find.globalFindClipboard,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true
});
@@ -336,13 +432,47 @@ export class StartFindAction extends EditorAction {
}
}
export class StartFindWithSelectionAction extends EditorAction {
constructor() {
super({
id: FIND_IDS.StartFindWithSelection,
label: nls.localize('startFindAction', "Find"),
alias: 'Find',
precondition: null,
kbOpts: {
kbExpr: null,
primary: null,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_E,
}
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = CommonFindController.get(editor);
if (controller) {
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: true,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true
});
controller.setGlobalBufferTerm(controller.getState().searchString);
}
}
}
export abstract class MatchFindAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = CommonFindController.get(editor);
if (controller && !this._run(controller)) {
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: (controller.getState().searchString.length === 0),
seedSearchStringFromSelection: (controller.getState().searchString.length === 0) && editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: true,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true
});
@@ -353,7 +483,6 @@ export abstract class MatchFindAction extends EditorAction {
protected abstract _run(controller: CommonFindController): boolean;
}
@editorAction
export class NextMatchFindAction extends MatchFindAction {
constructor() {
@@ -375,7 +504,6 @@ export class NextMatchFindAction extends MatchFindAction {
}
}
@editorAction
export class PreviousMatchFindAction extends MatchFindAction {
constructor() {
@@ -398,7 +526,7 @@ export class PreviousMatchFindAction extends MatchFindAction {
}
export abstract class SelectionMatchFindAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = CommonFindController.get(editor);
if (!controller) {
return;
@@ -411,6 +539,7 @@ export abstract class SelectionMatchFindAction extends EditorAction {
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true
});
@@ -421,7 +550,6 @@ export abstract class SelectionMatchFindAction extends EditorAction {
protected abstract _run(controller: CommonFindController): boolean;
}
@editorAction
export class NextSelectionMatchFindAction extends SelectionMatchFindAction {
constructor() {
@@ -442,7 +570,6 @@ export class NextSelectionMatchFindAction extends SelectionMatchFindAction {
}
}
@editorAction
export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction {
constructor() {
@@ -463,7 +590,6 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction {
}
}
@editorAction
export class StartFindReplaceAction extends EditorAction {
constructor() {
@@ -480,7 +606,7 @@ export class StartFindReplaceAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (editor.getConfiguration().readOnly) {
return;
}
@@ -489,7 +615,7 @@ export class StartFindReplaceAction extends EditorAction {
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;
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.
@@ -500,6 +626,7 @@ export class StartFindReplaceAction extends EditorAction {
controller.start({
forceRevealReplace: true,
seedSearchStringFromSelection: seedSearchStringFromSelection,
seedSearchStringFromGlobalClipboard: editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection,
shouldFocus: shouldFocus,
shouldAnimate: true
});
@@ -507,8 +634,6 @@ export class StartFindReplaceAction extends EditorAction {
}
}
@editorAction
export class ShowNextFindTermAction extends MatchFindAction {
constructor() {
@@ -518,7 +643,7 @@ export class ShowNextFindTermAction extends MatchFindAction {
alias: 'Show Next Find Term',
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus),
primary: ShowNextFindTermKeybinding.primary,
mac: ShowNextFindTermKeybinding.mac,
@@ -533,8 +658,7 @@ export class ShowNextFindTermAction extends MatchFindAction {
}
}
@editorAction
export class ShpwPreviousFindTermAction extends MatchFindAction {
export class ShowPreviousFindTermAction extends MatchFindAction {
constructor() {
super({
@@ -543,7 +667,7 @@ export class ShpwPreviousFindTermAction extends MatchFindAction {
alias: 'Find Show Previous Find Term',
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus),
primary: ShowPreviousFindTermKeybinding.primary,
mac: ShowPreviousFindTermKeybinding.mac,
@@ -558,26 +682,38 @@ export class ShpwPreviousFindTermAction extends MatchFindAction {
}
}
registerEditorContribution(FindController);
registerEditorAction(StartFindAction);
registerEditorAction(StartFindWithSelectionAction);
registerEditorAction(NextMatchFindAction);
registerEditorAction(PreviousMatchFindAction);
registerEditorAction(NextSelectionMatchFindAction);
registerEditorAction(PreviousSelectionMatchFindAction);
registerEditorAction(StartFindReplaceAction);
registerEditorAction(ShowNextFindTermAction);
registerEditorAction(ShowPreviousFindTermAction);
const FindCommand = EditorCommand.bindToContribution<CommonFindController>(CommonFindController.get);
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.CloseFindWidgetCommand,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.closeFindWidget(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.ToggleCaseSensitiveCommand,
precondition: null,
handler: x => x.toggleCaseSensitive(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: ToggleCaseSensitiveKeybinding.primary,
mac: ToggleCaseSensitiveKeybinding.mac,
@@ -586,12 +722,12 @@ CommonEditorRegistry.registerEditorCommand(new FindCommand({
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.ToggleWholeWordCommand,
precondition: null,
handler: x => x.toggleWholeWords(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: ToggleWholeWordKeybinding.primary,
mac: ToggleWholeWordKeybinding.mac,
@@ -600,12 +736,12 @@ CommonEditorRegistry.registerEditorCommand(new FindCommand({
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.ToggleRegexCommand,
precondition: null,
handler: x => x.toggleRegex(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: ToggleRegexKeybinding.primary,
mac: ToggleRegexKeybinding.mac,
@@ -614,12 +750,12 @@ CommonEditorRegistry.registerEditorCommand(new FindCommand({
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.ToggleSearchScopeCommand,
precondition: null,
handler: x => x.toggleSearchScope(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: ToggleSearchScopeKeybinding.primary,
mac: ToggleSearchScopeKeybinding.mac,
@@ -628,35 +764,35 @@ CommonEditorRegistry.registerEditorCommand(new FindCommand({
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceOneAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replace(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_1
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceAllAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replaceAll(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter
}
}));
CommonEditorRegistry.registerEditorCommand(new FindCommand({
registerEditorCommand(new FindCommand({
id: FIND_IDS.SelectAllMatchesAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.selectAllMatches(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(5),
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Alt | KeyCode.Enter
}
}));
}));

View File

@@ -11,10 +11,11 @@ import { Range } from 'vs/editor/common/core/range';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { editorFindMatchHighlight, editorFindMatch } from 'vs/platform/theme/common/colorRegistry';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export class FindDecorations implements IDisposable {
private _editor: editorCommon.ICommonCodeEditor;
private _editor: ICodeEditor;
private _decorations: string[];
private _overviewRulerApproximateDecorations: string[];
private _findScopeDecorationId: string;
@@ -22,7 +23,7 @@ export class FindDecorations implements IDisposable {
private _highlightedDecorationId: string;
private _startPosition: Position;
constructor(editor: editorCommon.ICommonCodeEditor) {
constructor(editor: ICodeEditor) {
this._editor = editor;
this._decorations = [];
this._overviewRulerApproximateDecorations = [];
@@ -220,7 +221,7 @@ export class FindDecorations implements IDisposable {
return result;
}
private static _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({
private static readonly _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'currentFindMatch',
showIfCollapsed: true,
@@ -231,7 +232,7 @@ export class FindDecorations implements IDisposable {
}
});
private static _FIND_MATCH_DECORATION = ModelDecorationOptions.register({
private static readonly _FIND_MATCH_DECORATION = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'findMatch',
showIfCollapsed: true,
@@ -242,13 +243,13 @@ export class FindDecorations implements IDisposable {
}
});
private static _FIND_MATCH_NO_OVERVIEW_DECORATION = ModelDecorationOptions.register({
private static readonly _FIND_MATCH_NO_OVERVIEW_DECORATION = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'findMatch',
showIfCollapsed: true
});
private static _FIND_MATCH_ONLY_OVERVIEW_DECORATION = ModelDecorationOptions.register({
private static readonly _FIND_MATCH_ONLY_OVERVIEW_DECORATION = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
overviewRuler: {
color: themeColorFromId(editorFindMatchHighlight),
@@ -257,13 +258,13 @@ export class FindDecorations implements IDisposable {
}
});
private static _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({
private static readonly _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'rangeHighlight',
isWholeLine: true
});
private static _FIND_SCOPE_DECORATION = ModelDecorationOptions.register({
private static readonly _FIND_SCOPE_DECORATION = ModelDecorationOptions.register({
className: 'findScope',
isWholeLine: true
});

View File

@@ -6,7 +6,7 @@
import { RunOnceScheduler } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/common/replacePattern';
import { ReplacePattern, parseReplaceString } from 'vs/editor/contrib/find/replacePattern';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
@@ -20,6 +20,13 @@ import { Constants } from 'vs/editor/common/core/uint';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
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';
export const CONTEXT_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('findWidgetVisible', false);
export const CONTEXT_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = CONTEXT_FIND_WIDGET_VISIBLE.toNegated();
// Keep ContextKey use of 'Focussed' to not break when clauses
export const CONTEXT_FIND_INPUT_FOCUSED = new RawContextKey<boolean>('findInputFocussed', false);
export const ToggleCaseSensitiveKeybinding: IKeybindings = {
primary: KeyMod.Alt | KeyCode.KEY_C,
@@ -46,6 +53,7 @@ export const ShowNextFindTermKeybinding: IKeybindings = {
export const FIND_IDS = {
StartFindAction: 'actions.find',
StartFindWithSelection: 'actions.findWithSelection',
NextMatchFindAction: 'editor.action.nextMatchFindAction',
PreviousMatchFindAction: 'editor.action.previousMatchFindAction',
NextSelectionMatchFindAction: 'editor.action.nextSelectionMatchFindAction',
@@ -67,7 +75,7 @@ export const MATCHES_LIMIT = 19999;
export class FindModelBoundToEditorModel {
private _editor: editorCommon.ICommonCodeEditor;
private _editor: ICodeEditor;
private _state: FindReplaceState;
private _toDispose: IDisposable[];
private _decorations: FindDecorations;
@@ -76,7 +84,7 @@ export class FindModelBoundToEditorModel {
private _updateDecorationsScheduler: RunOnceScheduler;
private _isDisposed: boolean;
constructor(editor: editorCommon.ICommonCodeEditor, state: FindReplaceState) {
constructor(editor: ICodeEditor, state: FindReplaceState) {
this._editor = editor;
this._state = state;
this._toDispose = [];
@@ -111,7 +119,7 @@ export class FindModelBoundToEditorModel {
this._updateDecorationsScheduler.schedule();
}));
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._toDispose.push(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
this.research(false, this._state.searchScope);
}
@@ -487,4 +495,4 @@ export class FindModelBoundToEditorModel {
this._ignoreModelContentChanged = false;
}
}
}
}

View File

@@ -9,8 +9,8 @@ import * as dom from 'vs/base/browser/dom';
import { Widget } from 'vs/base/browser/ui/widget';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { FIND_IDS } from 'vs/editor/contrib/find/common/findModel';
import { FindReplaceState } from 'vs/editor/contrib/find/common/findState';
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';
@@ -18,7 +18,7 @@ import { inputActiveOptionBorder, editorWidgetBackground, contrastBorder, widget
export class FindOptionsWidget extends Widget implements IOverlayWidget {
private static ID = 'editor.contrib.findOptionsWidget';
private static readonly ID = 'editor.contrib.findOptionsWidget';
private _editor: ICodeEditor;
private _state: FindReplaceState;
@@ -88,7 +88,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
this._editor.addOverlayWidget(this);
this._register(this._state.addChangeListener((e) => {
this._register(this._state.onFindReplaceStateChange((e) => {
let somethingChanged = false;
if (e.isRegex) {
this.regex.checked = this._state.isRegex;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { EventEmitter } from 'vs/base/common/eventEmitter';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
@@ -56,9 +56,6 @@ function effectiveOptionValue(override: FindOptionOverride, value: boolean): boo
}
export class FindReplaceState implements IDisposable {
private static _CHANGED_EVENT = 'changed';
private _searchString: string;
private _replaceString: string;
private _isRevealed: boolean;
@@ -73,7 +70,7 @@ export class FindReplaceState implements IDisposable {
private _matchesPosition: number;
private _matchesCount: number;
private _currentMatch: Range;
private _eventEmitter: EventEmitter;
private _onFindReplaceStateChange: Emitter<FindReplaceStateChangedEvent>;
public get searchString(): string { return this._searchString; }
public get replaceString(): string { return this._replaceString; }
@@ -82,10 +79,16 @@ export class FindReplaceState implements IDisposable {
public get isRegex(): boolean { return effectiveOptionValue(this._isRegexOverride, this._isRegex); }
public get wholeWord(): boolean { return effectiveOptionValue(this._wholeWordOverride, this._wholeWord); }
public get matchCase(): boolean { return effectiveOptionValue(this._matchCaseOverride, this._matchCase); }
public get actualIsRegex(): boolean { return this._isRegex; }
public get actualWholeWord(): boolean { return this._wholeWord; }
public get actualMatchCase(): boolean { return this._matchCase; }
public get searchScope(): Range { 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 onFindReplaceStateChange(): Event<FindReplaceStateChangedEvent> { return this._onFindReplaceStateChange.event; }
constructor() {
this._searchString = '';
@@ -102,15 +105,10 @@ export class FindReplaceState implements IDisposable {
this._matchesPosition = 0;
this._matchesCount = 0;
this._currentMatch = null;
this._eventEmitter = new EventEmitter();
this._onFindReplaceStateChange = new Emitter<FindReplaceStateChangedEvent>();
}
public dispose(): void {
this._eventEmitter.dispose();
}
public addChangeListener(listener: (e: FindReplaceStateChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(FindReplaceState._CHANGED_EVENT, listener);
}
public changeMatchInfo(matchesPosition: number, matchesCount: number, currentMatch: Range): void {
@@ -158,7 +156,7 @@ export class FindReplaceState implements IDisposable {
}
if (somethingChanged) {
this._eventEmitter.emit(FindReplaceState._CHANGED_EVENT, changeEvent);
this._onFindReplaceStateChange.fire(changeEvent);
}
}
@@ -248,7 +246,7 @@ export class FindReplaceState implements IDisposable {
}
if (somethingChanged) {
this._eventEmitter.emit(FindReplaceState._CHANGED_EVENT, changeEvent);
this._onFindReplaceStateChange.fire(changeEvent);
}
}
}

View File

@@ -21,11 +21,10 @@ 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 } from 'vs/editor/contrib/find/common/findModel';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/common/findState';
import { FIND_IDS, MATCHES_LIMIT, CONTEXT_FIND_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 { CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/common/findController';
import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
@@ -35,6 +34,7 @@ import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, ac
export interface IFindController {
replace(): void;
replaceAll(): void;
getGlobalBufferTerm(): string;
}
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
@@ -81,7 +81,7 @@ export class FindWidgetViewZone implements IViewZone {
}
export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider {
private static ID = 'editor.contrib.findWidget';
private static readonly ID = 'editor.contrib.findWidget';
private _codeEditor: ICodeEditor;
private _state: FindReplaceState;
private _controller: IFindController;
@@ -110,6 +110,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _viewZoneId: number;
private _resizeSash: Sash;
private _resized: boolean;
constructor(
codeEditor: ICodeEditor,
@@ -130,50 +131,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._isVisible = false;
this._isReplaceVisible = false;
this._register(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
this._buildDomNode();
this._updateButtons();
let checkEditorWidth = () => {
let editorWidth = this._codeEditor.getConfiguration().layoutInfo.width;
let minimapWidth = this._codeEditor.getConfiguration().layoutInfo.minimapWidth;
let collapsedFindWidget = false;
let reducedFindWidget = false;
let narrowFindWidget = false;
let widgetWidth = dom.getTotalWidth(this._domNode);
if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) {
// as the widget is resized by users, we may need to change the max width of the widget as the editor width changes.
this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`;
this._replaceInputBox.inputElement.style.width = `${dom.getTotalWidth(this._findInput.inputBox.inputElement)}px`;
return;
}
if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth >= editorWidth) {
reducedFindWidget = true;
}
if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth) {
narrowFindWidget = true;
}
if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth + 50) {
collapsedFindWidget = true;
}
dom.toggleClass(this._domNode, 'collapsed-find-widget', collapsedFindWidget);
dom.toggleClass(this._domNode, 'narrow-find-widget', narrowFindWidget);
dom.toggleClass(this._domNode, 'reduced-find-widget', reducedFindWidget);
if (!narrowFindWidget && !collapsedFindWidget) {
// the minimal left offset of findwidget is 15px.
this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`;
}
let findInputWidth = dom.getTotalWidth(this._findInput.inputBox.inputElement);
if (findInputWidth > 0) {
this._replaceInputBox.inputElement.style.width = `${findInputWidth}px`;
}
};
checkEditorWidth();
this._tryUpdateWidgetWidth();
this._register(this._codeEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
if (e.readOnly) {
@@ -184,7 +145,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._updateButtons();
}
if (e.layoutInfo) {
checkEditorWidth();
this._tryUpdateWidgetWidth();
}
}));
this._register(this._codeEditor.onDidChangeCursorSelection(() => {
@@ -192,9 +153,18 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._updateToggleSelectionFindButton();
}
}));
this._register(this._codeEditor.onDidFocusEditor(() => {
if (this._isVisible) {
let globalBufferTerm = this._controller.getGlobalBufferTerm();
if (globalBufferTerm && globalBufferTerm !== this._state.searchString) {
this._state.change({ searchString: globalBufferTerm }, true);
this._findInput.select();
}
}
}));
this._findInputFocused = CONTEXT_FIND_INPUT_FOCUSED.bindTo(contextKeyService);
this._focusTracker = this._register(dom.trackFocus(this._findInput.inputBox.inputElement));
this._focusTracker.addFocusListener(() => {
this._register(this._focusTracker.onDidFocus(() => {
this._findInputFocused.set(true);
if (this._toggleSelectionFind.checked) {
@@ -210,10 +180,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}
}
});
this._focusTracker.addBlurListener(() => {
}));
this._register(this._focusTracker.onDidBlur(() => {
this._findInputFocused.set(false);
});
}));
this._codeEditor.addOverlayWidget(this);
this._viewZone = new FindWidgetViewZone(0); // Put it before the first line then users can scroll beyond the first line.
@@ -290,6 +260,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (this._state.isReplaceRevealed) {
if (!this._codeEditor.getConfiguration().readOnly && !this._isReplaceVisible) {
this._isReplaceVisible = true;
this._replaceInputBox.width = this._findInput.inputBox.width;
this._updateButtons();
}
} else {
@@ -405,6 +376,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
} else {
this._toggleSelectionFind.checked = false;
}
this._tryUpdateWidgetWidth();
this._updateButtons();
setTimeout(() => {
@@ -535,6 +507,53 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._replaceInputBox.style(inputStyles);
}
private _tryUpdateWidgetWidth() {
if (!this._isVisible) {
return;
}
let editorWidth = this._codeEditor.getConfiguration().layoutInfo.width;
let minimapWidth = this._codeEditor.getConfiguration().layoutInfo.minimapWidth;
let collapsedFindWidget = false;
let reducedFindWidget = false;
let narrowFindWidget = false;
if (this._resized) {
let widgetWidth = dom.getTotalWidth(this._domNode);
if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) {
// as the widget is resized by users, we may need to change the max width of the widget as the editor width changes.
this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`;
this._replaceInputBox.inputElement.style.width = `${dom.getTotalWidth(this._findInput.inputBox.inputElement)}px`;
return;
}
}
if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth >= editorWidth) {
reducedFindWidget = true;
}
if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth) {
narrowFindWidget = true;
}
if (FIND_WIDGET_INITIAL_WIDTH + 28 + minimapWidth - MAX_MATCHES_COUNT_WIDTH >= editorWidth + 50) {
collapsedFindWidget = true;
}
dom.toggleClass(this._domNode, 'collapsed-find-widget', collapsedFindWidget);
dom.toggleClass(this._domNode, 'narrow-find-widget', narrowFindWidget);
dom.toggleClass(this._domNode, 'reduced-find-widget', reducedFindWidget);
if (!narrowFindWidget && !collapsedFindWidget) {
// the minimal left offset of findwidget is 15px.
this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`;
}
if (this._resized) {
let findInputWidth = dom.getTotalWidth(this._findInput.inputBox.inputElement);
if (findInputWidth > 0) {
this._replaceInputBox.inputElement.style.width = `${findInputWidth}px`;
}
}
}
// ----- Public
public focusFindInput(): void {
@@ -862,13 +881,15 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _buildSash() {
this._resizeSash = new Sash(this._domNode, this, { orientation: Orientation.VERTICAL });
this._resized = false;
let originalWidth = FIND_WIDGET_INITIAL_WIDTH;
this._register(this._resizeSash.addListener('start', (e: ISashEvent) => {
this._register(this._resizeSash.onDidStart((e: ISashEvent) => {
originalWidth = dom.getTotalWidth(this._domNode);
}));
this._register(this._resizeSash.addListener('change', (evt: ISashEvent) => {
this._register(this._resizeSash.onDidChange((evt: ISashEvent) => {
this._resized = true;
let width = originalWidth + evt.startX - evt.currentX;
if (width < FIND_WIDGET_INITIAL_WIDTH) {
@@ -1076,4 +1097,3 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`);
}
});

View File

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 517 B

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

View File

Before

Width:  |  Height:  |  Size: 307 B

After

Width:  |  Height:  |  Size: 307 B

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

View File

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 131 B

View File

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 131 B

View File

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

View File

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 637 B

View File

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 637 B

View File

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View File

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View File

@@ -30,7 +30,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand {
if (this._ranges.length > 0) {
// Collect all edit operations
var ops: IEditOperation[] = [];
for (var i = 0; i < this._ranges.length; i++) {
for (let i = 0; i < this._ranges.length; i++) {
ops.push({
range: this._ranges[i],
text: this._replaceStrings[i]
@@ -45,7 +45,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand {
// Merge operations that touch each other
var resultOps: IEditOperation[] = [];
var previousOp = ops[0];
for (var i = 1; i < ops.length; i++) {
for (let i = 1; i < ops.length; i++) {
if (previousOp.range.endLineNumber === ops[i].range.startLineNumber && previousOp.range.endColumn === ops[i].range.startColumn) {
// These operations are one after another and can be merged
previousOp.range = previousOp.range.plusRange(ops[i].range);

View File

@@ -113,12 +113,12 @@ export abstract class SimpleFindWidget extends Widget {
});
this._focusTracker = this._register(dom.trackFocus(this._innerDomNode));
this._register(this._focusTracker.addFocusListener(this.onFocusTrackerFocus.bind(this)));
this._register(this._focusTracker.addBlurListener(this.onFocusTrackerBlur.bind(this)));
this._register(this._focusTracker.onDidFocus(this.onFocusTrackerFocus.bind(this)));
this._register(this._focusTracker.onDidBlur(this.onFocusTrackerBlur.bind(this)));
this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode));
this._register(this._findInputFocusTracker.addFocusListener(this.onFindInputFocusTrackerFocus.bind(this)));
this._register(this._findInputFocusTracker.addBlurListener(this.onFindInputFocusTrackerBlur.bind(this)));
this._register(this._findInputFocusTracker.onDidFocus(this.onFindInputFocusTrackerFocus.bind(this)));
this._register(this._findInputFocusTracker.onDidBlur(this.onFindInputFocusTrackerBlur.bind(this)));
this._register(dom.addDisposableListener(this._innerDomNode, 'click', (event) => {
event.stopPropagation();

View File

@@ -7,16 +7,14 @@
import * as assert from 'assert';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import {
getSelectionSearchString
} from 'vs/editor/contrib/find/common/find';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { getSelectionSearchString } from 'vs/editor/contrib/find/findController';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
suite('Find', () => {
test('search string at position', () => {
withMockCodeEditor([
withTestCodeEditor([
'ABC DEF',
'0123 456'
], {}, (editor, cursor) => {
@@ -39,7 +37,7 @@ suite('Find', () => {
});
test('search string with selection', () => {
withMockCodeEditor([
withTestCodeEditor([
'ABC DEF',
'0123 456'
], {}, (editor, cursor) => {
@@ -63,7 +61,7 @@ suite('Find', () => {
});
test('search string with multiline selection', () => {
withMockCodeEditor([
withTestCodeEditor([
'ABC DEF',
'0123 456'
], {}, (editor, cursor) => {
@@ -86,4 +84,4 @@ suite('Find', () => {
});
});
});
});

View File

@@ -11,14 +11,16 @@ 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 { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, StartFindAction } from 'vs/editor/contrib/find/common/findController';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import * as platform from 'vs/base/common/platform';
import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, StartFindAction } from 'vs/editor/contrib/find/findController';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { HistoryNavigator } from 'vs/base/common/history';
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 { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { Delayer } from 'vs/base/common/async';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export class TestFindController extends CommonFindController {
@@ -28,8 +30,13 @@ export class TestFindController extends CommonFindController {
private _delayedUpdateHistoryEvent: Emitter<void> = new Emitter<void>();
constructor(editor: ICommonCodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IStorageService storageService: IStorageService) {
super(editor, contextKeyService, storageService);
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IClipboardService clipboardService: IClipboardService
) {
super(editor, contextKeyService, storageService, clipboardService);
this._updateHistoryDelayer = new Delayer<void>(50);
}
@@ -70,21 +77,104 @@ function fromRange(rng: Range): number[] {
suite('FindController', () => {
let queryState: { [key: string]: any; } = {};
let clipboardState = '';
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, <any>{
serviceCollection.set(IStorageService, {
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
});
} as IStorageService);
test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => {
withMockCodeEditor([
if (platform.isMacintosh) {
serviceCollection.set(IClipboardService, <any>{
readFindText: _ => clipboardState,
writeFindText: (value: any) => { clipboardState = value; }
});
}
test('stores to the global clipboard buffer on start find action', () => {
withTestCodeEditor([
'ABC',
'ABC',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
if (!platform.isMacintosh) {
assert.ok(true);
return;
}
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
// I select ABC on the first line
editor.setSelection(new Selection(1, 1, 1, 4));
// I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor);
assert.deepEqual(findController.getGlobalBufferTerm(), findController.getState().searchString);
findController.dispose();
});
});
test('reads from the global clipboard buffer on next find action if buffer exists', () => {
withTestCodeEditor([
'ABC',
'ABC',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = 'ABC';
if (!platform.isMacintosh) {
assert.ok(true);
return;
}
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findState = findController.getState();
let nextMatchFindAction = new NextMatchFindAction();
nextMatchFindAction.run(null, editor);
assert.equal(findState.searchString, 'ABC');
assert.deepEqual(fromRange(editor.getSelection()), [1, 1, 1, 4]);
findController.dispose();
});
});
test('writes to the global clipboard buffer when text changes', () => {
withTestCodeEditor([
'ABC',
'ABC',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
if (!platform.isMacintosh) {
assert.ok(true);
return;
}
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findState = findController.getState();
findState.change({ searchString: 'ABC' }, true);
assert.deepEqual(findController.getGlobalBufferTerm(), 'ABC');
findController.dispose();
});
});
test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => {
withTestCodeEditor([
'ABC',
'ABC',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findState = findController.getState();
@@ -136,10 +226,10 @@ suite('FindController', () => {
});
test('issue #3090: F3 does not loop with two matches on a single line', () => {
withMockCodeEditor([
withTestCodeEditor([
'import nls = require(\'vs/nls\');'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let nextMatchFindAction = new NextMatchFindAction();
@@ -159,12 +249,12 @@ suite('FindController', () => {
});
test('issue #6149: Auto-escape highlighted text for search and replace regex mode', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
let nextMatchFindAction = new NextMatchFindAction();
@@ -185,16 +275,17 @@ suite('FindController', () => {
});
test('issue #9043: Clear search scope when find widget is hidden', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchStringFromGlobalClipboard: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false
});
@@ -213,12 +304,12 @@ suite('FindController', () => {
});
test('find term is added to history on state change', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
@@ -229,12 +320,12 @@ suite('FindController', () => {
});
test('find term is added with delay', (done) => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.delayUpdateHistory = true;
findController.getState().change({ searchString: '1' }, false);
@@ -249,12 +340,12 @@ suite('FindController', () => {
});
test('show previous find term', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
@@ -266,12 +357,12 @@ suite('FindController', () => {
});
test('show previous find term do not update history', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
@@ -283,12 +374,12 @@ suite('FindController', () => {
});
test('show next find term', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
@@ -303,12 +394,12 @@ suite('FindController', () => {
});
test('show next find term do not update history', () => {
withMockCodeEditor([
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
@@ -323,10 +414,10 @@ suite('FindController', () => {
});
test('issue #18111: Regex replace with single space replaces with no space', () => {
withMockCodeEditor([
withTestCodeEditor([
'HRESULT OnAmbientPropertyChange(DISPID dispid);'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
@@ -348,12 +439,12 @@ suite('FindController', () => {
});
test('issue #24714: Regular expression with ^ in search & replace', () => {
withMockCodeEditor([
withTestCodeEditor([
'',
'line2',
'line3'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
@@ -392,14 +483,14 @@ suite('FindController query options persistence', () => {
queryState['editor.matchCase'] = false;
queryState['editor.wholeWord'] = false;
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, <any>{
serviceCollection.set(IStorageService, {
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
});
} as IStorageService);
test('matchCase', () => {
withMockCodeEditor([
withTestCodeEditor([
'abc',
'ABC',
'XYZ',
@@ -426,7 +517,7 @@ suite('FindController query options persistence', () => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
test('wholeWord', () => {
withMockCodeEditor([
withTestCodeEditor([
'ABC',
'AB',
'XYZ',
@@ -451,7 +542,7 @@ suite('FindController query options persistence', () => {
});
test('toggling options is saved', () => {
withMockCodeEditor([
withTestCodeEditor([
'ABC',
'AB',
'XYZ',
@@ -466,4 +557,4 @@ suite('FindController query options persistence', () => {
findController.dispose();
});
});
});
});

View File

@@ -9,17 +9,17 @@ 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 { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/common/findModel';
import { FindReplaceState } from 'vs/editor/contrib/find/common/findState';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { CoreNavigationCommands } from 'vs/editor/common/controller/coreCommands';
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';
suite('FindModel', () => {
function findTest(testName: string, callback: (editor: ICommonCodeEditor, cursor: Cursor) => void): void {
function findTest(testName: string, callback: (editor: ICodeEditor, cursor: Cursor) => void): void {
test(testName, () => {
withMockCodeEditor([
withTestCodeEditor([
'// my cool header',
'#include "cool.h"',
'#include <iostream>',
@@ -40,7 +40,7 @@ suite('FindModel', () => {
return [rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn];
}
function _getFindState(editor: ICommonCodeEditor) {
function _getFindState(editor: ICodeEditor) {
let model = editor.getModel();
let currentFindMatches: Range[] = [];
let allFindMatches: Range[] = [];
@@ -63,7 +63,7 @@ suite('FindModel', () => {
};
}
function assertFindState(editor: ICommonCodeEditor, cursor: number[], highlighted: number[], findDecorations: number[][]): void {
function assertFindState(editor: ICodeEditor, cursor: number[], highlighted: number[], findDecorations: number[][]): void {
assert.deepEqual(fromRange(editor.getSelection()), cursor, 'cursor');
let expectedState = {

View File

@@ -5,7 +5,7 @@
'use strict';
import * as assert from 'assert';
import { parseReplaceString, ReplacePattern, ReplacePiece } from 'vs/editor/contrib/find/common/replacePattern';
import { parseReplaceString, ReplacePattern, ReplacePiece } from 'vs/editor/contrib/find/replacePattern';
suite('Replace Pattern test', () => {

View File

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 456 B

View File

Before

Width:  |  Height:  |  Size: 444 B

After

Width:  |  Height:  |  Size: 444 B

View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

View File

Before

Width:  |  Height:  |  Size: 287 B

After

Width:  |  Height:  |  Size: 287 B

View File

@@ -24,7 +24,7 @@
}
.monaco-editor .margin-view-overlays:hover .folding,
.monaco-editor.alwaysShowFoldIcons .margin-view-overlays .folding {
.monaco-editor .margin-view-overlays .folding.alwaysShowFoldIcons {
opacity: 1;
}

View File

@@ -8,32 +8,32 @@
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import * as dom from 'vs/base/browser/dom';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { RunOnceScheduler, Delayer } 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 { ICommonCodeEditor, ScrollType, IModel } from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { ScrollType, IModel, IEditorContribution } from 'vs/editor/common/editorCommon';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp } from 'vs/editor/contrib/folding/common/foldingModel';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel';
import { FoldingDecorationProvider } from './foldingDecorations';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/common/hiddenRangeModel';
import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel';
import { IRange } from 'vs/editor/common/core/range';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { computeRanges as computeIndentRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
export const ID = 'editor.contrib.folding';
@editorContribution
export class FoldingController {
export class FoldingController implements IEditorContribution {
static MAX_FOLDING_REGIONS = 5000;
public static get(editor: ICommonCodeEditor): FoldingController {
public static get(editor: ICodeEditor): FoldingController {
return editor.getContribution<FoldingController>(ID);
}
@@ -63,7 +63,7 @@ export class FoldingController {
this.globalToDispose = [];
this.localToDispose = [];
this.foldingDecorationProvider = new FoldingDecorationProvider();
this.foldingDecorationProvider = new FoldingDecorationProvider(editor);
this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls;
this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged()));
@@ -166,7 +166,10 @@ export class FoldingController {
}
private computeRanges(editorModel: IModel) {
let ranges = editorModel.getIndentRanges();
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
let ranges = computeIndentRanges(editorModel, offSide, markers);
return ranges;
}
@@ -175,18 +178,27 @@ export class FoldingController {
}
private onModelContentChanged() {
this.foldingModelPromise = this.updateScheduler.trigger(() => {
if (this.foldingModel) { // null if editor has been disposed, or folding turned off
this.foldingModel.update(this.computeRanges(this.foldingModel.textModel));
}
return this.foldingModel;
});
if (this.updateScheduler) {
this.foldingModelPromise = this.updateScheduler.trigger(() => {
if (this.foldingModel) { // null if editor has been disposed, or folding turned off
// 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(this.computeRanges(this.foldingModel.textModel), selectionLineNumbers);
}
return this.foldingModel;
});
}
}
private onHiddenRangesChanges(hiddenRanges: IRange[]) {
let selections = this.editor.getSelections();
if (this.hiddenRangeModel.adjustSelections(selections)) {
this.editor.setSelections(selections);
if (hiddenRanges.length) {
let selections = this.editor.getSelections();
if (selections) {
if (this.hiddenRangeModel.adjustSelections(selections)) {
this.editor.setSelections(selections);
}
}
}
this.editor.setHiddenAreas(hiddenRanges);
}
@@ -201,11 +213,13 @@ export class FoldingController {
this.getFoldingModel().then(foldingModel => { // null is returned if folding got disabled in the meantime
if (foldingModel) {
let selections = this.editor.getSelections();
for (let selection of selections) {
let lineNumber = selection.selectionStartLineNumber;
if (this.hiddenRangeModel.isHidden(lineNumber)) {
let toToggle = foldingModel.getAllRegionsAtLine(lineNumber, r => r.isCollapsed && lineNumber > r.startLineNumber);
foldingModel.toggleCollapseState(toToggle);
if (selections) {
for (let selection of selections) {
let lineNumber = selection.selectionStartLineNumber;
if (this.hiddenRangeModel.isHidden(lineNumber)) {
let toToggle = foldingModel.getAllRegionsAtLine(lineNumber, r => r.isCollapsed && lineNumber > r.startLineNumber);
foldingModel.toggleCollapseState(toToggle);
}
}
}
}
@@ -219,10 +233,10 @@ export class FoldingController {
this.mouseDownInfo = null;
let range = e.target.range;
if (!range) {
if (!this.hiddenRangeModel || !range) {
return;
}
if (!e.event.leftButton) {
if (!e.event.leftButton && !e.event.middleButton) {
return;
}
let iconClicked = false;
@@ -239,21 +253,20 @@ export class FoldingController {
iconClicked = true;
break;
case MouseTargetType.CONTENT_EMPTY: {
let model = this.editor.getModel();
if (range.startColumn === model.getLineMaxColumn(range.startLineNumber)) {
let editorCoords = dom.getDomNodePagePosition(this.editor.getDomNode());
let pos = this.editor.getScrolledVisiblePosition(range.getEndPosition());
if (e.event.posy > editorCoords.top + pos.top + pos.height) {
return;
if (this.hiddenRangeModel.hasRanges()) {
const data = e.target.detail as IEmptyContentData;
if (!data.isAfterLines) {
break;
}
break;
}
return;
}
case MouseTargetType.CONTENT_TEXT: {
let model = this.editor.getModel();
if (range.startColumn === model.getLineMaxColumn(range.startLineNumber)) {
break;
if (this.hiddenRangeModel.hasRanges()) {
let model = this.editor.getModel();
if (model && range.startColumn === model.getLineMaxColumn(range.startLineNumber)) {
break;
}
}
return;
}
@@ -291,8 +304,13 @@ export class FoldingController {
if (foldingModel) {
let region = foldingModel.getRegionAtLine(lineNumber);
if (region && region.startLineNumber === lineNumber) {
if (iconClicked || region.isCollapsed) {
foldingModel.toggleCollapseState([region]);
let isCollapsed = region.isCollapsed;
if (iconClicked || isCollapsed) {
let toToggle = [region];
if (e.event.middleButton || e.event.shiftKey) {
toToggle.push(...foldingModel.getRegionsInside(region, r => r.isCollapsed === isCollapsed));
}
foldingModel.toggleCollapseState(toToggle);
this.reveal(lineNumber);
}
}
@@ -307,9 +325,9 @@ export class FoldingController {
abstract class FoldingAction<T> extends EditorAction {
abstract invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, args: T): void;
abstract invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: T): void;
public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: T): void | TPromise<void> {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: T): void | TPromise<void> {
let foldingController = FoldingController.get(editor);
if (!foldingController) {
return;
@@ -322,18 +340,19 @@ abstract class FoldingAction<T> extends EditorAction {
});
}
protected getSelectedLines(editor: ICommonCodeEditor) {
return editor.getSelections().map(s => s.startLineNumber);
protected getSelectedLines(editor: ICodeEditor) {
let selections = editor.getSelections();
return selections ? selections.map(s => s.startLineNumber) : [];
}
protected getLineNumbers(args: FoldingArguments, editor: ICommonCodeEditor) {
protected getLineNumbers(args: FoldingArguments, editor: ICodeEditor) {
if (args && args.selectionLines) {
return args.selectionLines.map(l => l + 1); // to 0-bases line numbers
}
return this.getSelectedLines(editor);
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
}
}
@@ -362,7 +381,6 @@ function foldingArgumentsConstraint(args: any) {
return true;
}
@editorAction
class UnfoldAction extends FoldingAction<FoldingArguments> {
constructor() {
@@ -395,7 +413,7 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, 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') {
@@ -406,7 +424,6 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
}
}
@editorAction
class UnFoldRecursivelyAction extends FoldingAction<void> {
constructor() {
@@ -422,12 +439,11 @@ class UnFoldRecursivelyAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, args: any): void {
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: any): void {
setCollapseStateLevelsDown(foldingModel, false, Number.MAX_VALUE, this.getSelectedLines(editor));
}
}
@editorAction
class FoldAction extends FoldingAction<FoldingArguments> {
constructor() {
@@ -449,7 +465,7 @@ class FoldAction extends FoldingAction<FoldingArguments> {
{
name: 'Fold editor argument',
description: `Property-value pairs that can be passed through this argument:
* 'levels': Number of levels to fold. Defauts to 1
* 'levels': Number of levels to fold. Defaults to 1
* 'direction': If 'up', folds given number of levels up otherwise folds down
* 'selectionLines': The start lines (0-based) of the editor selections to apply the fold action to. If not set, the active selection(s) will be used.
`,
@@ -460,7 +476,7 @@ class FoldAction extends FoldingAction<FoldingArguments> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, 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') {
@@ -471,7 +487,6 @@ class FoldAction extends FoldingAction<FoldingArguments> {
}
}
@editorAction
class FoldRecursivelyAction extends FoldingAction<void> {
constructor() {
@@ -487,7 +502,7 @@ class FoldRecursivelyAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void {
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
let selectedLines = this.getSelectedLines(editor);
setCollapseStateLevelsDown(foldingModel, true, Number.MAX_VALUE, selectedLines);
if (selectedLines.length > 0) {
@@ -497,7 +512,78 @@ class FoldRecursivelyAction extends FoldingAction<void> {
}
}
@editorAction
class FoldAllBlockCommentsAction extends FoldingAction<void> {
constructor() {
super({
id: 'editor.foldAllBlockComments',
label: nls.localize('foldAllBlockComments.label', "Fold All Block Comments"),
alias: 'Fold All Block Comments',
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH)
}
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
let comments = LanguageConfigurationRegistry.getComments(editor.getModel().getLanguageIdentifier().id);
if (comments && comments.blockCommentStartToken) {
let regExp = new RegExp('^\\s*' + escapeRegExpCharacters(comments.blockCommentStartToken));
setCollapseStateForMatchingLines(foldingModel, regExp, true);
}
}
}
class FoldAllRegionsAction extends FoldingAction<void> {
constructor() {
super({
id: 'editor.foldAllMarkerRegions',
label: nls.localize('foldAllMarkerRegions.label', "Fold All Regions"),
alias: 'Fold All Regions',
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8)
}
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editor.getModel().getLanguageIdentifier().id);
if (foldingRules && foldingRules.markers && foldingRules.markers.start) {
let regExp = new RegExp(foldingRules.markers.start);
setCollapseStateForMatchingLines(foldingModel, regExp, true);
}
}
}
class UnfoldAllRegionsAction extends FoldingAction<void> {
constructor() {
super({
id: 'editor.unfoldAllMarkerRegions',
label: nls.localize('unfoldAllMarkerRegions.label', "Unfold All Regions"),
alias: 'Unfold All Regions',
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9)
}
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editor.getModel().getLanguageIdentifier().id);
if (foldingRules && foldingRules.markers && foldingRules.markers.start) {
let regExp = new RegExp(foldingRules.markers.start);
setCollapseStateForMatchingLines(foldingModel, regExp, false);
}
}
}
class FoldAllAction extends FoldingAction<void> {
constructor() {
@@ -513,12 +599,11 @@ class FoldAllAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void {
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
setCollapseStateLevelsDown(foldingModel, true);
}
}
@editorAction
class UnfoldAllAction extends FoldingAction<void> {
constructor() {
@@ -534,26 +619,37 @@ class UnfoldAllAction extends FoldingAction<void> {
});
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void {
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
setCollapseStateLevelsDown(foldingModel, false);
}
}
class FoldLevelAction extends FoldingAction<void> {
private static ID_PREFIX = 'editor.foldLevel';
public static ID = (level: number) => FoldLevelAction.ID_PREFIX + level;
private static readonly ID_PREFIX = 'editor.foldLevel';
public static readonly ID = (level: number) => FoldLevelAction.ID_PREFIX + level;
private getFoldingLevel() {
return parseInt(this.id.substr(FoldLevelAction.ID_PREFIX.length));
}
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void {
invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void {
setCollapseStateAtLevel(foldingModel, this.getFoldingLevel(), true, this.getSelectedLines(editor));
}
}
for (let i = 1; i <= 9; i++) {
CommonEditorRegistry.registerEditorAction(
registerEditorContribution(FoldingController);
registerEditorAction(UnfoldAction);
registerEditorAction(UnFoldRecursivelyAction);
registerEditorAction(FoldAction);
registerEditorAction(FoldRecursivelyAction);
registerEditorAction(FoldAllAction);
registerEditorAction(UnfoldAllAction);
registerEditorAction(FoldAllBlockCommentsAction);
registerEditorAction(FoldAllRegionsAction);
registerEditorAction(UnfoldAllRegionsAction);
for (let i = 1; i <= 7; i++) {
registerInstantiatedEditorAction(
new FoldLevelAction({
id: FoldLevelAction.ID(i),
label: nls.localize('foldLevelAction.label', "Fold Level {0}", i),
@@ -565,4 +661,4 @@ for (let i = 1; i <= 9; i++) {
}
})
);
};
}

View File

@@ -3,9 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/editorCommon';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { FoldingRegion, IDecorationProvider } from 'vs/editor/contrib/folding/common/foldingModel';
import { IDecorationProvider } from 'vs/editor/contrib/folding/foldingModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export class FoldingDecorationProvider implements IDecorationProvider {
@@ -17,18 +18,21 @@ export class FoldingDecorationProvider implements IDecorationProvider {
private EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'folding autoHide'
linesDecorationsClassName: 'folding'
});
private EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'folding'
linesDecorationsClassName: 'folding alwaysShowFoldIcons'
});
public autoHideFoldingControls: boolean = true;
getDecorationOption(region: FoldingRegion): ModelDecorationOptions {
if (region.isCollapsed) {
constructor(private editor: ICodeEditor) {
}
getDecorationOption(isCollapsed: boolean): ModelDecorationOptions {
if (isCollapsed) {
return this.COLLAPSED_VISUAL_DECORATION;
} else if (this.autoHideFoldingControls) {
return this.EXPANDED_AUTO_HIDE_VISUAL_DECORATION;
@@ -36,4 +40,12 @@ export class FoldingDecorationProvider implements IDecorationProvider {
return this.EXPANDED_VISUAL_DECORATION;
}
}
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] {
return this.editor.deltaDecorations(oldDecorations, newDecorations);
}
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T {
return this.editor.changeDecorations(callback);
}
}

View File

@@ -3,17 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IModel, IModelDecorationOptions } from 'vs/editor/common/editorCommon';
import { IModel, IModelDecorationOptions, IModelDeltaDecoration, IModelDecorationsChangeAccessor } from 'vs/editor/common/editorCommon';
import Event, { Emitter } from 'vs/base/common/event';
import { IndentRanges } from 'vs/editor/common/model/indentRanges';
export interface ILineRange {
startLineNumber: number;
endLineNumber: number;
}
import { FoldingRanges, ILineRange, FoldingRegion } from './foldingRanges';
export interface IDecorationProvider {
getDecorationOption(region: FoldingRegion): IModelDecorationOptions;
getDecorationOption(isCollapsed: boolean): IModelDecorationOptions;
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[];
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T;
}
export interface FoldingModelChangeEvent {
@@ -27,18 +24,20 @@ export class FoldingModel {
private _textModel: IModel;
private _decorationProvider: IDecorationProvider;
private _regions: FoldingRegion[] = [];
private _ranges: IndentRanges;
private _ranges: FoldingRanges;
private _editorDecorationIds: string[];
private _updateEventEmitter = new Emitter<FoldingModelChangeEvent>();
public get regions(): FoldingRegion[] { return this._regions; };
public get onDidChange(): Event<FoldingModelChangeEvent> { return this._updateEventEmitter.event; };
public get ranges(): FoldingRanges { return this._ranges; }
public get onDidChange(): Event<FoldingModelChangeEvent> { return this._updateEventEmitter.event; }
public get textModel() { return this._textModel; }
constructor(textModel: IModel, decorationProvider: IDecorationProvider) {
this._textModel = textModel;
this._decorationProvider = decorationProvider;
this._ranges = new FoldingRanges(new Uint32Array(0), new Uint32Array(0));
this._editorDecorationIds = [];
}
public toggleCollapseState(regions: FoldingRegion[]) {
@@ -46,45 +45,39 @@ export class FoldingModel {
return;
}
let processed = {};
this._textModel.changeDecorations(accessor => {
this._decorationProvider.changeDecorations(accessor => {
for (let region of regions) {
if (region.editorDecorationId && !processed[region.editorDecorationId]) {
processed[region.editorDecorationId] = true;
region.isCollapsed = !region.isCollapsed;
accessor.changeDecorationOptions(region.editorDecorationId, this._decorationProvider.getDecorationOption(region));
let index = region.regionIndex;
let editorDecorationId = this._editorDecorationIds[index];
if (editorDecorationId && !processed[editorDecorationId]) {
processed[editorDecorationId] = true;
let newCollapseState = !this._ranges.isCollapsed(index);
this._ranges.setCollapsed(index, newCollapseState);
accessor.changeDecorationOptions(editorDecorationId, this._decorationProvider.getDecorationOption(newCollapseState));
}
}
});
this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions });
}
public update(newRanges: IndentRanges): void {
let editorDecorationIds = [];
public update(newRanges: FoldingRanges, blockedLineNumers: number[] = []): void {
let newEditorDecorations = [];
// remember the latest start line numbers of the collapsed regions
let collapsedStartLineNumbers: number[] = [];
for (let region of this._regions) {
if (region.editorDecorationId) {
if (region.isCollapsed) {
let decRange = this._textModel.getDecorationRange(region.editorDecorationId);
if (decRange) {
collapsedStartLineNumbers.push(decRange.startLineNumber);
}
let isBlocked = (startLineNumber, endLineNumber) => {
for (let blockedLineNumber of blockedLineNumers) {
if (startLineNumber < blockedLineNumber && blockedLineNumber <= endLineNumber) { // first line is visible
return true;
}
editorDecorationIds.push(region.editorDecorationId);
}
}
return false;
};
let recycleBin = this._regions;
let newRegions = [];
let newRegion = (ranges: IndentRanges, index: number, isCollapsed: boolean) => {
let region = recycleBin.length ? recycleBin.pop() : new FoldingRegion();
region.init(ranges, index, isCollapsed);
newRegions.push(region);
let startLineNumber = region.startLineNumber;
let initRange = (index: number, isCollapsed: boolean) => {
let startLineNumber = newRanges.getStartLineNumber(index);
if (isCollapsed && isBlocked(startLineNumber, newRanges.getEndLineNumber(index))) {
isCollapsed = false;
}
newRanges.setCollapsed(index, isCollapsed);
let maxColumn = this._textModel.getLineMaxColumn(startLineNumber);
let decorationRange = {
startLineNumber: startLineNumber,
@@ -92,38 +85,48 @@ export class FoldingModel {
endLineNumber: startLineNumber,
endColumn: maxColumn
};
newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(region) });
newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(isCollapsed) });
};
let k = 0, i = 0;
while (i < collapsedStartLineNumbers.length && k < newRanges.length) {
let collapsedStartLineNumber = collapsedStartLineNumbers[i];
while (k < newRanges.length && collapsedStartLineNumber > newRanges.getStartLineNumber(k)) {
newRegion(newRanges, k, false);
k++;
}
if (k < newRanges.length) {
let currStartLineNumber = newRanges.getStartLineNumber(k);
if (collapsedStartLineNumber < currStartLineNumber) {
i++;
} else if (collapsedStartLineNumber === currStartLineNumber) {
newRegion(newRanges, k, true);
i++;
k++;
let i = 0;
let nextCollapsed = () => {
while (i < this._ranges.length) {
let isCollapsed = this._ranges.isCollapsed(i);
i++;
if (isCollapsed) {
return i - 1;
}
}
return -1;
};
let k = 0;
let collapsedIndex = nextCollapsed();
while (collapsedIndex !== -1 && k < newRanges.length) {
// get the latest range
let decRange = this._textModel.getDecorationRange(this._editorDecorationIds[collapsedIndex]);
if (decRange) {
let collapsedStartLineNumber = decRange.startLineNumber;
if (this._textModel.getLineMaxColumn(collapsedStartLineNumber) === decRange.startColumn) { // test that the decoration is still at the end otherwise it got deleted
while (k < newRanges.length) {
let startLineNumber = newRanges.getStartLineNumber(k);
if (collapsedStartLineNumber >= startLineNumber) {
initRange(k, collapsedStartLineNumber === startLineNumber);
k++;
} else {
break;
}
}
}
}
collapsedIndex = nextCollapsed();
}
while (k < newRanges.length) {
newRegion(newRanges, k, false);
initRange(k, false);
k++;
}
let newEditorDecorationIds = this._textModel.deltaDecorations(editorDecorationIds, newEditorDecorations);
for (let i = 0; i < newEditorDecorations.length; i++) {
newRegions[i].editorDecorationId = newEditorDecorationIds[i];
}
this._regions = newRegions;
this._editorDecorationIds = this._decorationProvider.deltaDecorations(this._editorDecorationIds, newEditorDecorations);
this._ranges = newRanges;
this._updateEventEmitter.fire({ model: this });
}
@@ -133,12 +136,12 @@ export class FoldingModel {
*/
public getMemento(): CollapseMemento {
let collapsedRanges: ILineRange[] = [];
for (let region of this._regions) {
if (region.isCollapsed && region.editorDecorationId) {
let range = this._textModel.getDecorationRange(region.editorDecorationId);
for (let i = 0; i < this._ranges.length; i++) {
if (this._ranges.isCollapsed(i)) {
let range = this._textModel.getDecorationRange(this._editorDecorationIds[i]);
if (range) {
let startLineNumber = range.startLineNumber;
let endLineNumber = range.endLineNumber + region.endLineNumber - region.startLineNumber;
let endLineNumber = range.endLineNumber + this._ranges.getEndLineNumber(i) - this._ranges.getStartLineNumber(i);
collapsedRanges.push({ startLineNumber, endLineNumber });
}
}
@@ -167,13 +170,7 @@ export class FoldingModel {
}
public dispose() {
let editorDecorationIds = [];
for (let region of this._regions) {
if (region.editorDecorationId) {
editorDecorationIds.push(region.editorDecorationId);
}
}
this._textModel.deltaDecorations(editorDecorationIds, []);
this._decorationProvider.deltaDecorations(this._editorDecorationIds, []);
}
getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion, level: number) => boolean): FoldingRegion[] {
@@ -182,7 +179,7 @@ export class FoldingModel {
let index = this._ranges.findRange(lineNumber);
let level = 1;
while (index >= 0) {
let current = this._regions[index];
let current = this._ranges.toRegion(index);
if (!filter || filter(current, level)) {
result.push(current);
}
@@ -197,8 +194,8 @@ export class FoldingModel {
if (this._ranges) {
let index = this._ranges.findRange(lineNumber);
if (index >= 0) {
return this._regions[index];
};
return this._ranges.toRegion(index);
}
}
return null;
}
@@ -209,9 +206,9 @@ export class FoldingModel {
let levelStack: FoldingRegion[] = trackLevel ? [] : null;
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[i];
if (current.startLineNumber < endLineNumber) {
for (let i = index, len = this._ranges.length; i < len; i++) {
let current = this._ranges.toRegion(i);
if (this._ranges.getStartLineNumber(i) < endLineNumber) {
if (trackLevel) {
while (levelStack.length > 0 && !current.containedBy(levelStack[levelStack.length - 1])) {
levelStack.pop();
@@ -232,58 +229,7 @@ export class FoldingModel {
}
export class FoldingRegion {
public editorDecorationId: string;
public isCollapsed: boolean;
private index: number;
private indentRanges: IndentRanges;
constructor() {
}
public init(indentRanges: IndentRanges, index: number, isCollapsed: boolean): void {
this.indentRanges = indentRanges;
this.index = index;
this.isCollapsed = isCollapsed;
this.editorDecorationId = void 0;
}
public get startLineNumber() {
return this.indentRanges.getStartLineNumber(this.index);
}
public get endLineNumber() {
return this.indentRanges.getEndLineNumber(this.index);
}
public get regionIndex() {
return this.index;
}
public get parentIndex() {
return this.indentRanges.getParentIndex(this.index);
}
isAfterLine(lineNumber: number): boolean {
return lineNumber < this.startLineNumber;
}
isBeforeLine(lineNumber: number): boolean {
return lineNumber > this.endLineNumber;
}
contains(range: ILineRange): boolean {
return this.startLineNumber <= range.startLineNumber && this.endLineNumber >= range.endLineNumber;
}
containedBy(range: ILineRange): boolean {
return range.startLineNumber <= this.startLineNumber && range.endLineNumber >= this.endLineNumber;
}
containsLine(lineNumber: number) {
return this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber;
}
hidesLine(lineNumber: number) {
return this.startLineNumber < lineNumber && lineNumber <= this.endLineNumber;
}
}
/**
* Collapse or expand the regions at the given locations including all children.
@@ -335,7 +281,26 @@ export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse:
* @param blockedLineNumbers
*/
export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: number, doCollapse: boolean, blockedLineNumbers: number[]): void {
let filter = (region, level) => level === foldLevel && region.isCollapsed !== doCollapse && !blockedLineNumbers.some(line => region.containsLine(line));
let filter = (region: FoldingRegion, level: number) => level === foldLevel && region.isCollapsed !== doCollapse && !blockedLineNumbers.some(line => region.containsLine(line));
let toToggle = foldingModel.getRegionsInside(null, filter);
foldingModel.toggleCollapseState(toToggle);
}
/**
* Folds all regions for which the lines start with a given regex
* @param foldingModel the folding model
*/
export function setCollapseStateForMatchingLines(foldingModel: FoldingModel, regExp: RegExp, doCollapse: boolean): void {
let editorModel = foldingModel.textModel;
let ranges = foldingModel.ranges;
let toToggle = [];
for (let i = ranges.length - 1; i >= 0; i--) {
if (doCollapse !== ranges.isCollapsed(i)) {
let startLineNumber = ranges.getStartLineNumber(i);
if (regExp.test(editorModel.getLineContent(startLineNumber))) {
toToggle.push(ranges.toRegion(i));
}
}
}
foldingModel.toggleCollapseState(toToggle);
}

View File

@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export interface ILineRange {
startLineNumber: number;
endLineNumber: number;
}
export const MAX_FOLDING_REGIONS = 0xFFFF;
export const MAX_LINE_NUMBER = 0xFFFFFF;
const MASK_INDENT = 0xFF000000;
export class FoldingRanges {
private _startIndexes: Uint32Array;
private _endIndexes: Uint32Array;
private _collapseStates: Uint32Array;
private _parentsComputed: boolean;
constructor(startIndexes: Uint32Array, endIndexes: Uint32Array) {
if (startIndexes.length !== endIndexes.length || startIndexes.length > MAX_FOLDING_REGIONS) {
throw new Error('invalid startIndexes or endIndexes size');
}
this._startIndexes = startIndexes;
this._endIndexes = endIndexes;
this._collapseStates = new Uint32Array(Math.ceil(startIndexes.length / 32));
}
private ensureParentIndices() {
if (!this._parentsComputed) {
this._parentsComputed = true;
let parentIndexes = [];
let isInsideLast = (startLineNumber: number, endLineNumber: number) => {
let index = parentIndexes[parentIndexes.length - 1];
return this.getStartLineNumber(index) <= startLineNumber && this.getEndLineNumber(index) >= endLineNumber;
};
for (let i = 0, len = this._startIndexes.length; i < len; i++) {
let startLineNumber = this._startIndexes[i];
let endLineNumber = this._endIndexes[i];
if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
throw new Error('startLineNumber or endLineNumber must not exceed ' + MAX_LINE_NUMBER);
}
while (parentIndexes.length > 0 && !isInsideLast(startLineNumber, endLineNumber)) {
parentIndexes.pop();
}
let parentIndex = parentIndexes.length > 0 ? parentIndexes[parentIndexes.length - 1] : -1;
parentIndexes.push(i);
this._startIndexes[i] = startLineNumber + ((parentIndex & 0xFF) << 24);
this._endIndexes[i] = endLineNumber + ((parentIndex & 0xFF00) << 16);
}
}
}
public get length(): number {
return this._startIndexes.length;
}
public getStartLineNumber(index: number): number {
return this._startIndexes[index] & MAX_LINE_NUMBER;
}
public getEndLineNumber(index: number): number {
return this._endIndexes[index] & MAX_LINE_NUMBER;
}
public isCollapsed(index: number): boolean {
let arrayIndex = (index / 32) | 0;
let bit = index % 32;
return (this._collapseStates[arrayIndex] & (1 << bit)) !== 0;
}
public setCollapsed(index: number, newState: boolean) {
let arrayIndex = (index / 32) | 0;
let bit = index % 32;
let value = this._collapseStates[arrayIndex];
if (newState) {
this._collapseStates[arrayIndex] = value | (1 << bit);
} else {
this._collapseStates[arrayIndex] = value & ~(1 << bit);
}
}
public toRegion(index: number): FoldingRegion {
return new FoldingRegion(this, index);
}
public getParentIndex(index: number) {
this.ensureParentIndices();
let parent = ((this._startIndexes[index] & MASK_INDENT) >>> 24) + ((this._endIndexes[index] & MASK_INDENT) >>> 16);
if (parent === MAX_FOLDING_REGIONS) {
return -1;
}
return parent;
}
public contains(index: number, line: number) {
return this.getStartLineNumber(index) <= line && this.getEndLineNumber(index) >= line;
}
private findIndex(line: number) {
let low = 0, high = this._startIndexes.length;
if (high === 0) {
return -1; // no children
}
while (low < high) {
let mid = Math.floor((low + high) / 2);
if (line < this.getStartLineNumber(mid)) {
high = mid;
} else {
low = mid + 1;
}
}
return low - 1;
}
public findRange(line: number): number {
let index = this.findIndex(line);
if (index >= 0) {
let endLineNumber = this.getEndLineNumber(index);
if (endLineNumber >= line) {
return index;
}
index = this.getParentIndex(index);
while (index !== -1) {
if (this.contains(index, line)) {
return index;
}
index = this.getParentIndex(index);
}
}
return -1;
}
}
export class FoldingRegion {
constructor(private ranges: FoldingRanges, private index: number) {
}
public get startLineNumber() {
return this.ranges.getStartLineNumber(this.index);
}
public get endLineNumber() {
return this.ranges.getEndLineNumber(this.index);
}
public get regionIndex() {
return this.index;
}
public get parentIndex() {
return this.ranges.getParentIndex(this.index);
}
public get isCollapsed() {
return this.ranges.isCollapsed(this.index);
}
containedBy(range: ILineRange): boolean {
return range.startLineNumber <= this.startLineNumber && range.endLineNumber >= this.endLineNumber;
}
containsLine(lineNumber: number) {
return this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber;
}
hidesLine(lineNumber: number) {
return this.startLineNumber < lineNumber && lineNumber <= this.endLineNumber;
}
}

View File

@@ -5,7 +5,7 @@
import Event, { Emitter } from 'vs/base/common/event';
import { Range, IRange } from 'vs/editor/common/core/range';
import { FoldingRegion, FoldingModel, CollapseMemento } from 'vs/editor/contrib/folding/common/foldingModel';
import { FoldingModel, CollapseMemento } from 'vs/editor/contrib/folding/foldingModel';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Selection } from 'vs/editor/common/core/selection';
import { findFirst } from 'vs/base/common/arrays';
@@ -16,13 +16,13 @@ export class HiddenRangeModel {
private _foldingModelListener: IDisposable;
private _updateEventEmitter = new Emitter<IRange[]>();
public get onDidChange(): Event<IRange[]> { return this._updateEventEmitter.event; };
public get onDidChange(): Event<IRange[]> { return this._updateEventEmitter.event; }
public get hiddenRanges() { return this._hiddenRanges; }
public constructor(model: FoldingModel) {
this._foldingModel = model;
this._foldingModelListener = model.onDidChange(_ => this.updateHiddenRanges());
if (model.regions.length) {
if (model.ranges.length) {
this.updateHiddenRanges();
}
}
@@ -31,27 +31,36 @@ export class HiddenRangeModel {
let updateHiddenAreas = false;
let newHiddenAreas: IRange[] = [];
let i = 0; // index into hidden
let k = 0;
let lastCollapsed: FoldingRegion = null;
let lastCollapsedStart = Number.MAX_VALUE;
let lastCollapsedEnd = -1;
let regions = this._foldingModel.regions;
for (let region of regions) {
if (!region.isCollapsed || lastCollapsed && lastCollapsed.contains(region)) {
let ranges = this._foldingModel.ranges;
for (; i < ranges.length; i++) {
if (!ranges.isCollapsed(i)) {
continue;
}
let startLineNumber = ranges.getStartLineNumber(i) + 1; // the first line is not hidden
let endLineNumber = ranges.getEndLineNumber(i);
if (lastCollapsedStart <= startLineNumber && endLineNumber <= lastCollapsedEnd) {
// ignore ranges contained in collapsed regions
continue;
}
lastCollapsed = region;
let range = region;
if (!updateHiddenAreas && i < this._hiddenRanges.length && matchesHiddenRange(this._hiddenRanges[i], range)) {
newHiddenAreas.push(this._hiddenRanges[i]);
i++;
if (!updateHiddenAreas && k < this._hiddenRanges.length && this._hiddenRanges[k].startLineNumber === startLineNumber && this._hiddenRanges[k].endLineNumber === endLineNumber) {
// reuse the old ranges
newHiddenAreas.push(this._hiddenRanges[k]);
k++;
} else {
updateHiddenAreas = true;
newHiddenAreas.push(new Range(range.startLineNumber + 1, 1, range.endLineNumber, 1));
newHiddenAreas.push(new Range(startLineNumber, 1, endLineNumber, 1));
}
};
if (updateHiddenAreas || i < this._hiddenRanges.length) {
lastCollapsedStart = startLineNumber;
lastCollapsedEnd = endLineNumber;
}
if (updateHiddenAreas || k < this._hiddenRanges.length) {
this.applyHiddenRanges(newHiddenAreas);
}
}
@@ -128,9 +137,6 @@ export class HiddenRangeModel {
}
}
function matchesHiddenRange(hr: IRange, range: FoldingRegion) {
return hr.startLineNumber === range.startLineNumber + 1 && hr.endLineNumber === range.endLineNumber;
}
function isInside(line: number, range: IRange) {
return line >= range.startLineNumber && line <= range.endLineNumber;
}

View File

@@ -0,0 +1,161 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITextModel } from 'vs/editor/common/editorCommon';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
import { computeIndentLevel } from 'vs/editor/common/model/modelLine';
import { FoldingRanges, MAX_LINE_NUMBER } from 'vs/editor/contrib/folding/foldingRanges';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
// public only for testing
export class RangesCollector {
private _startIndexes: number[];
private _endIndexes: number[];
private _indentOccurrences: number[];
private _length: number;
private _FoldingRangesLimit: number;
constructor(FoldingRangesLimit: number) {
this._startIndexes = [];
this._endIndexes = [];
this._indentOccurrences = [];
this._length = 0;
this._FoldingRangesLimit = FoldingRangesLimit;
}
public insertFirst(startLineNumber: number, endLineNumber: number, indent: number) {
if (startLineNumber > MAX_LINE_NUMBER || endLineNumber > MAX_LINE_NUMBER) {
return;
}
let index = this._length;
this._startIndexes[index] = startLineNumber;
this._endIndexes[index] = endLineNumber;
this._length++;
if (indent < 1000) {
this._indentOccurrences[indent] = (this._indentOccurrences[indent] || 0) + 1;
}
}
public toIndentRanges(model: ITextModel) {
if (this._length <= this._FoldingRangesLimit) {
// reverse and create arrays of the exact length
let startIndexes = new Uint32Array(this._length);
let endIndexes = new Uint32Array(this._length);
for (let i = this._length - 1, k = 0; i >= 0; i-- , k++) {
startIndexes[k] = this._startIndexes[i];
endIndexes[k] = this._endIndexes[i];
}
return new FoldingRanges(startIndexes, endIndexes);
} else {
let entries = 0;
let maxIndent = this._indentOccurrences.length;
for (let i = 0; i < this._indentOccurrences.length; i++) {
let n = this._indentOccurrences[i];
if (n) {
if (n + entries > this._FoldingRangesLimit) {
maxIndent = i;
break;
}
entries += n;
}
}
const tabSize = model.getOptions().tabSize;
// reverse and create arrays of the exact length
let startIndexes = new Uint32Array(entries);
let endIndexes = new Uint32Array(entries);
for (let i = this._length - 1, k = 0; i >= 0; i--) {
let startIndex = this._startIndexes[i];
let lineContent = model.getLineContent(startIndex);
let indent = computeIndentLevel(lineContent, tabSize);
if (indent < maxIndent) {
startIndexes[k] = startIndex;
endIndexes[k] = this._endIndexes[i];
k++;
}
}
return new FoldingRanges(startIndexes, endIndexes);
}
}
}
interface PreviousRegion { indent: number; line: number; marker: boolean; }
export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, FoldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRanges {
const tabSize = model.getOptions().tabSize;
let result = new RangesCollector(FoldingRangesLimit);
let pattern = void 0;
if (markers) {
pattern = new RegExp(`(${markers.start.source})|(?:${markers.end.source})`);
}
let previousRegions: PreviousRegion[] = [];
previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: false }); // sentinel, to make sure there's at least one entry
for (let line = model.getLineCount(); line > 0; line--) {
let lineContent = model.getLineContent(line);
let indent = computeIndentLevel(lineContent, tabSize);
let previous = previousRegions[previousRegions.length - 1];
if (indent === -1) {
if (offSide && !previous.marker) {
// for offSide languages, empty lines are associated to the next block
previous.line = line;
}
continue; // only whitespace
}
let m;
if (pattern && (m = lineContent.match(pattern))) {
// folding pattern match
if (m[1]) { // start pattern match
// discard all regions until the folding pattern
let i = previousRegions.length - 1;
while (i > 0 && !previousRegions[i].marker) {
i--;
}
if (i > 0) {
previousRegions.length = i + 1;
previous = previousRegions[i];
// new folding range from pattern, includes the end line
result.insertFirst(line, previous.line, indent);
previous.marker = false;
previous.indent = indent;
previous.line = line;
continue;
} else {
// no end marker found, treat line as a regular line
}
} else { // end pattern match
previousRegions.push({ indent: -2, line, marker: true });
continue;
}
}
if (previous.indent > indent) {
// discard all regions with larger indent
do {
previousRegions.pop();
previous = previousRegions[previousRegions.length - 1];
} while (previous.indent > indent);
// new folding range
let endLineNumber = previous.line - 1;
if (endLineNumber - line >= 1) { // needs at east size 1
result.insertFirst(line, endLineNumber, indent);
}
}
if (previous.indent === indent) {
previous.line = line;
} else { // previous.indent < indent
// new region with a bigger indent
previousRegions.push({ indent, line, marker: false });
}
}
return result.toIndentRanges(model);
}

View File

@@ -5,13 +5,16 @@
'use strict';
import * as assert from 'assert';
import { FoldingModel, FoldingRegion, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp } from 'vs/editor/contrib/folding/common/foldingModel';
import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel';
import { Model } from 'vs/editor/common/model/model';
import { computeRanges } from 'vs/editor/common/model/indentRanges';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
import { TrackedRangeStickiness, IModelDeltaDecoration, IModel, IModelDecorationsChangeAccessor } from 'vs/editor/common/editorCommon';
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 { FoldingRegion } from 'vs/editor/contrib/folding/foldingRanges';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
interface ExpectedRegion {
@@ -27,9 +30,20 @@ export class TestDecorationProvider {
linesDecorationsClassName: 'folding'
});
getDecorationOption(region: FoldingRegion): ModelDecorationOptions {
constructor(private model: IModel) {
}
getDecorationOption(isCollapsed: boolean): ModelDecorationOptions {
return this.testDecorator;
}
deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] {
return this.model.deltaDecorations(oldDecorations, newDecorations);
}
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T {
return this.model.changeDecorations(callback);
}
}
suite('Folding Model', () => {
@@ -46,8 +60,24 @@ suite('Folding Model', () => {
}
}
function assertFoldedRegions(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
assert.deepEqual(foldingModel.regions.filter(r => r.isCollapsed).map(r => ({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber, isCollapsed: false })), expectedRegions, message);
function assertFoldedRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
let actualRanges = [];
let actual = foldingModel.ranges;
for (let i = 0; i < actual.length; i++) {
if (actual.isCollapsed(i)) {
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i)));
}
}
assert.deepEqual(actualRanges, expectedRegions, message);
}
function assertRanges(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) {
let actualRanges = [];
let actual = foldingModel.ranges;
for (let i = 0; i < actual.length; i++) {
actualRanges.push(r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.isCollapsed(i)));
}
assert.deepEqual(actualRanges, expectedRegions, message);
}
function assertRegions(actual: FoldingRegion[], expectedRegions: ExpectedRegion[], message?: string) {
@@ -67,7 +97,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
foldingModel.update(ranges);
@@ -76,7 +106,7 @@ suite('Folding Model', () => {
let r2 = r(4, 7, false);
let r3 = r(5, 6, false);
assertRegions(foldingModel.regions, [r1, r2, r3]);
assertRanges(foldingModel, [r1, r2, r3]);
assertRegion(foldingModel.getRegionAtLine(1), r1, '1');
assertRegion(foldingModel.getRegionAtLine(2), r1, '2');
@@ -106,7 +136,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
foldingModel.update(ranges);
@@ -115,22 +145,22 @@ suite('Folding Model', () => {
let r2 = r(4, 7, false);
let r3 = r(5, 6, false);
assertRegions(foldingModel.regions, [r1, r2, r3]);
assertRanges(foldingModel, [r1, r2, r3]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)]);
foldingModel.update(ranges);
assertRegions(foldingModel.regions, [r(1, 3, true), r2, r3]);
assertRanges(foldingModel, [r(1, 3, true), r2, r3]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(5)]);
foldingModel.update(ranges);
assertRegions(foldingModel.regions, [r(1, 3, true), r2, r(5, 6, true)]);
assertRanges(foldingModel, [r(1, 3, true), r2, r(5, 6, true)]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(7)]);
foldingModel.update(ranges);
assertRegions(foldingModel.regions, [r(1, 3, true), r(4, 7, true), r(5, 6, true)]);
assertRanges(foldingModel, [r(1, 3, true), r(4, 7, true), r(5, 6, true)]);
textModel.dispose();
} finally {
@@ -152,7 +182,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
foldingModel.update(ranges);
@@ -161,14 +191,56 @@ suite('Folding Model', () => {
let r2 = r(4, 7, false);
let r3 = r(5, 6, false);
assertRegions(foldingModel.regions, [r1, r2, r3]);
assertRanges(foldingModel, [r1, r2, r3]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2), foldingModel.getRegionAtLine(5)]);
textModel.applyEdits([EditOperation.insert(new Position(4, 1), '//hello\n')]);
foldingModel.update(computeRanges(textModel, false, null));
assertRegions(foldingModel.regions, [r(1, 3, true), r(5, 8, false), r(6, 7, true)]);
assertRanges(foldingModel, [r(1, 3, true), r(5, 8, false), r(6, 7, true)]);
} finally {
textModel.dispose();
}
});
test('delete', () => {
let lines = [
/* 1*/ 'function foo() {',
/* 2*/ ' switch (x) {',
/* 3*/ ' case 1:',
/* 4*/ ' //hello1',
/* 5*/ ' break;',
/* 6*/ ' case 2:',
/* 7*/ ' //hello2',
/* 8*/ ' break;',
/* 9*/ ' case 3:',
/* 10*/ ' //hello3',
/* 11*/ ' break;',
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
foldingModel.update(ranges);
let r1 = r(1, 12, false);
let r2 = r(2, 11, false);
let r3 = r(3, 5, false);
let r4 = r(6, 8, false);
let r5 = r(9, 11, false);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(6)]);
textModel.applyEdits([EditOperation.delete(new Range(6, 11, 9, 0))]);
foldingModel.update(computeRanges(textModel, false, null));
assertRanges(foldingModel, [r(1, 9, false), r(2, 8, false), r(3, 5, false), r(6, 8, false)]);
} finally {
textModel.dispose();
}
@@ -187,7 +259,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, null);
foldingModel.update(ranges);
@@ -196,7 +268,7 @@ suite('Folding Model', () => {
let r2 = r(4, 7, false);
let r3 = r(5, 6, false);
assertRegions(foldingModel.regions, [r1, r2, r3]);
assertRanges(foldingModel, [r1, r2, r3]);
let region1 = foldingModel.getRegionAtLine(r1.startLineNumber);
let region2 = foldingModel.getRegionAtLine(r2.startLineNumber);
let region3 = foldingModel.getRegionAtLine(r3.startLineNumber);
@@ -229,7 +301,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
foldingModel.update(ranges);
@@ -244,7 +316,7 @@ suite('Folding Model', () => {
let region2 = foldingModel.getRegionAtLine(r2.startLineNumber);
let region3 = foldingModel.getRegionAtLine(r3.startLineNumber);
assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
assertRegions(foldingModel.getRegionsInside(null, (r, level) => level === 1), [r1, r2], '1');
assertRegions(foldingModel.getRegionsInside(null, (r, level) => level === 2), [r3], '2');
@@ -279,7 +351,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
foldingModel.update(ranges);
@@ -289,7 +361,7 @@ suite('Folding Model', () => {
let r3 = r(3, 7, false);
let r4 = r(4, 5, false);
assertRegions(foldingModel.regions, [r1, r2, r3, r4]);
assertRanges(foldingModel, [r1, r2, r3, r4]);
assertRegions(foldingModel.getAllRegionsAtLine(1), [r1], '1');
assertRegions(foldingModel.getAllRegionsAtLine(2), [r1, r2].reverse(), '2');
@@ -325,7 +397,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
foldingModel.update(ranges);
@@ -335,25 +407,25 @@ suite('Folding Model', () => {
let r3 = r(4, 11, false);
let r4 = r(5, 6, false);
let r5 = r(9, 10, false);
assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
setCollapseStateLevelsDown(foldingModel, true, Number.MAX_VALUE, [4]);
assertFoldedRegions(foldingModel, [r3, r4, r5], '1');
assertFoldedRanges(foldingModel, [r3, r4, r5], '1');
setCollapseStateLevelsDown(foldingModel, false, Number.MAX_VALUE, [8]);
assertFoldedRegions(foldingModel, [], '2');
assertFoldedRanges(foldingModel, [], '2');
setCollapseStateLevelsDown(foldingModel, true, Number.MAX_VALUE, [12]);
assertFoldedRegions(foldingModel, [r2, r3, r4, r5], '1');
assertFoldedRanges(foldingModel, [r2, r3, r4, r5], '1');
setCollapseStateLevelsDown(foldingModel, false, Number.MAX_VALUE, [7]);
assertFoldedRegions(foldingModel, [r2], '1');
assertFoldedRanges(foldingModel, [r2], '1');
setCollapseStateLevelsDown(foldingModel, false);
assertFoldedRegions(foldingModel, [], '1');
assertFoldedRanges(foldingModel, [], '1');
setCollapseStateLevelsDown(foldingModel, true);
assertFoldedRegions(foldingModel, [r1, r2, r3, r4, r5], '1');
assertFoldedRanges(foldingModel, [r1, r2, r3, r4, r5], '1');
} finally {
textModel.dispose();
}
@@ -374,39 +446,46 @@ suite('Folding Model', () => {
/* 10*/ ' return;',
/* 11*/ ' }',
/* 12*/ ' }',
/* 13*/ '}'];
/* 13*/ ' //#region',
/* 14*/ ' const bar = 9;',
/* 15*/ ' //#endregion',
/* 16*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
let ranges = computeRanges(textModel, false, { start: /^\s*\/\/#region$/, end: /^\s*\/\/#endregion$/ });
foldingModel.update(ranges);
let r1 = r(1, 2, false);
let r2 = r(3, 12, false);
let r2 = r(3, 15, false);
let r3 = r(4, 11, false);
let r4 = r(5, 6, false);
let r5 = r(9, 10, false);
assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]);
let r6 = r(13, 15, false);
assertRanges(foldingModel, [r1, r2, r3, r4, r5, r6]);
setCollapseStateAtLevel(foldingModel, 1, true, []);
assertFoldedRegions(foldingModel, [r1, r2], '1');
assertFoldedRanges(foldingModel, [r1, r2], '1');
setCollapseStateAtLevel(foldingModel, 1, false, [5]);
assertFoldedRegions(foldingModel, [r2], '1');
assertFoldedRanges(foldingModel, [r2], '2');
setCollapseStateAtLevel(foldingModel, 1, false, [1]);
assertFoldedRegions(foldingModel, [], '1');
assertFoldedRanges(foldingModel, [], '3');
setCollapseStateAtLevel(foldingModel, 2, true, []);
assertFoldedRegions(foldingModel, [r3], '1');
assertFoldedRanges(foldingModel, [r3, r6], '4');
setCollapseStateAtLevel(foldingModel, 2, false, [5, 6]);
assertFoldedRanges(foldingModel, [r3], '5');
setCollapseStateAtLevel(foldingModel, 3, true, [4, 9]);
assertFoldedRegions(foldingModel, [r3, r4], '1');
assertFoldedRanges(foldingModel, [r3, r4], '6');
setCollapseStateAtLevel(foldingModel, 3, false, [4, 9]);
assertFoldedRegions(foldingModel, [r3], '1');
assertFoldedRanges(foldingModel, [r3], '7');
} finally {
textModel.dispose();
}
@@ -430,7 +509,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
foldingModel.update(ranges);
@@ -440,25 +519,25 @@ suite('Folding Model', () => {
let r3 = r(4, 11, false);
let r4 = r(5, 6, false);
let r5 = r(9, 10, false);
assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
setCollapseStateLevelsDown(foldingModel, true, 1, [4]);
assertFoldedRegions(foldingModel, [r3], '1');
assertFoldedRanges(foldingModel, [r3], '1');
setCollapseStateLevelsDown(foldingModel, true, 2, [4]);
assertFoldedRegions(foldingModel, [r3, r4, r5], '2');
assertFoldedRanges(foldingModel, [r3, r4, r5], '2');
setCollapseStateLevelsDown(foldingModel, false, 2, [3]);
assertFoldedRegions(foldingModel, [r4, r5], '3');
assertFoldedRanges(foldingModel, [r4, r5], '3');
setCollapseStateLevelsDown(foldingModel, false, 2, [2]);
assertFoldedRegions(foldingModel, [r4, r5], '4');
assertFoldedRanges(foldingModel, [r4, r5], '4');
setCollapseStateLevelsDown(foldingModel, true, 4, [2]);
assertFoldedRegions(foldingModel, [r1, r4, r5], '5');
assertFoldedRanges(foldingModel, [r1, r4, r5], '5');
setCollapseStateLevelsDown(foldingModel, false, 4, [2, 3]);
assertFoldedRegions(foldingModel, [], '6');
assertFoldedRanges(foldingModel, [], '6');
} finally {
textModel.dispose();
}
@@ -482,7 +561,7 @@ suite('Folding Model', () => {
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
foldingModel.update(ranges);
@@ -492,19 +571,59 @@ suite('Folding Model', () => {
let r3 = r(4, 11, false);
let r4 = r(5, 6, false);
let r5 = r(9, 10, false);
assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
setCollapseStateLevelsUp(foldingModel, true, 1, [4]);
assertFoldedRegions(foldingModel, [r3], '1');
assertFoldedRanges(foldingModel, [r3], '1');
setCollapseStateLevelsUp(foldingModel, true, 2, [4]);
assertFoldedRegions(foldingModel, [r2, r3], '2');
assertFoldedRanges(foldingModel, [r2, r3], '2');
setCollapseStateLevelsUp(foldingModel, false, 4, [1, 3, 4]);
assertFoldedRegions(foldingModel, [], '3');
assertFoldedRanges(foldingModel, [], '3');
setCollapseStateLevelsUp(foldingModel, true, 2, [10]);
assertFoldedRegions(foldingModel, [r3, r5], '4');
assertFoldedRanges(foldingModel, [r3, r5], '4');
} finally {
textModel.dispose();
}
});
test('setCollapseStateForMatchingLines', () => {
let lines = [
/* 1*/ '/**',
/* 2*/ ' * the class',
/* 3*/ ' */',
/* 4*/ 'class A {',
/* 5*/ ' /**',
/* 6*/ ' * the foo',
/* 7*/ ' */',
/* 8*/ ' void foo() {',
/* 9*/ ' /*',
/* 10*/ ' * the comment',
/* 11*/ ' */',
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
foldingModel.update(ranges);
let r1 = r(1, 3, false);
let r2 = r(4, 12, false);
let r3 = r(5, 7, false);
let r4 = r(8, 11, false);
let r5 = r(9, 11, false);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
let regExp = new RegExp('^\\s*' + escapeRegExpCharacters('/*'));
setCollapseStateForMatchingLines(foldingModel, regExp, true);
assertFoldedRanges(foldingModel, [r1, r3, r5], '1');
} finally {
textModel.dispose();
}

View File

@@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------------------
* 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 { Model } from 'vs/editor/common/model/model';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
import { MAX_FOLDING_REGIONS } from 'vs/editor/contrib/folding/foldingRanges';
let markers: FoldingMarkers = {
start: /^\s*#region\b/,
end: /^\s*#endregion\b/
};
suite('FoldingRanges', () => {
test('test max folding regions', () => {
let lines = [];
let nRegions = MAX_FOLDING_REGIONS;
for (let i = 0; i < nRegions; i++) {
lines.push('#region');
}
for (let i = 0; i < nRegions; i++) {
lines.push('#endregion');
}
let model = Model.createFromString(lines.join('\n'));
let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
assert.equal(actual.length, nRegions, 'len');
for (let i = 0; i < nRegions; i++) {
assert.equal(actual.getStartLineNumber(i), i + 1, 'start' + i);
assert.equal(actual.getEndLineNumber(i), nRegions * 2 - i, 'end' + i);
assert.equal(actual.getParentIndex(i), i - 1, 'parent' + i);
}
});
test('findRange', () => {
let lines = [
/* 1*/ '#region',
/* 2*/ '#endregion',
/* 3*/ 'class A {',
/* 4*/ ' void foo() {',
/* 5*/ ' if (true) {',
/* 6*/ ' return;',
/* 7*/ ' }',
/* 8*/ '',
/* 9*/ ' if (true) {',
/* 10*/ ' return;',
/* 11*/ ' }',
/* 12*/ ' }',
/* 13*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
try {
let actual = computeRanges(textModel, false, markers);
// let r0 = r(1, 2);
// let r1 = r(3, 12);
// let r2 = r(4, 11);
// let r3 = r(5, 6);
// let r4 = r(9, 10);
assert.equal(actual.findRange(1), 0, '1');
assert.equal(actual.findRange(2), 0, '2');
assert.equal(actual.findRange(3), 1, '3');
assert.equal(actual.findRange(4), 2, '4');
assert.equal(actual.findRange(5), 3, '5');
assert.equal(actual.findRange(6), 3, '6');
assert.equal(actual.findRange(7), 2, '7');
assert.equal(actual.findRange(8), 2, '8');
assert.equal(actual.findRange(9), 4, '9');
assert.equal(actual.findRange(10), 4, '10');
assert.equal(actual.findRange(11), 2, '11');
assert.equal(actual.findRange(12), 1, '12');
assert.equal(actual.findRange(13), -1, '13');
} finally {
textModel.dispose();
}
});
test('setCollapsed', () => {
let lines = [];
let nRegions = 500;
for (let i = 0; i < nRegions; i++) {
lines.push('#region');
}
for (let i = 0; i < nRegions; i++) {
lines.push('#endregion');
}
let model = Model.createFromString(lines.join('\n'));
let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS);
assert.equal(actual.length, nRegions, 'len');
for (let i = 0; i < nRegions; i++) {
actual.setCollapsed(i, i % 3 === 0);
}
for (let i = 0; i < nRegions; i++) {
assert.equal(actual.isCollapsed(i), i % 3 === 0, 'line' + i);
}
});
});

View File

@@ -5,11 +5,11 @@
'use strict';
import * as assert from 'assert';
import { FoldingModel } from 'vs/editor/contrib/folding/common/foldingModel';
import { FoldingModel } from 'vs/editor/contrib/folding/foldingModel';
import { Model } from 'vs/editor/common/model/model';
import { computeRanges } from 'vs/editor/common/model/indentRanges';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { TestDecorationProvider } from './foldingModel.test';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/common/hiddenRangeModel';
import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel';
import { IRange } from 'vs/editor/common/core/range';
@@ -41,7 +41,7 @@ suite('Hidden Range Model', () => {
/* 10*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider());
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let hiddenRangeModel = new HiddenRangeModel(foldingModel);
assert.equal(hiddenRangeModel.hasRanges(), false);

View File

@@ -5,7 +5,7 @@
'use strict';
import * as assert from 'assert';
import { computeRanges } from 'vs/editor/common/model/indentRanges';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { Model } from 'vs/editor/common/model/model';
interface IndentRange {

View File

@@ -0,0 +1,322 @@
/*---------------------------------------------------------------------------------------------
* 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 { Model } from 'vs/editor/common/model/model';
import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration';
interface ExpectedIndentRange {
startLineNumber: number;
endLineNumber: number;
parentIndex: number;
}
function assertRanges(lines: string[], expected: ExpectedIndentRange[], offside: boolean, markers?: FoldingMarkers): void {
let model = Model.createFromString(lines.join('\n'));
let actual = computeRanges(model, offside, markers);
let actualRanges = [];
for (let i = 0; i < actual.length; i++) {
actualRanges[i] = r(actual.getStartLineNumber(i), actual.getEndLineNumber(i), actual.getParentIndex(i));
}
assert.deepEqual(actualRanges, expected);
model.dispose();
}
function r(startLineNumber: number, endLineNumber: number, parentIndex: number, marker = false): ExpectedIndentRange {
return { startLineNumber, endLineNumber, parentIndex };
}
suite('Indentation Folding', () => {
test('Fold one level', () => {
let range = [
'A',
' A',
' A',
' A'
];
assertRanges(range, [r(1, 4, -1)], true);
assertRanges(range, [r(1, 4, -1)], false);
});
test('Fold two levels', () => {
let range = [
'A',
' A',
' A',
' A',
' A'
];
assertRanges(range, [r(1, 5, -1), r(3, 5, 0)], true);
assertRanges(range, [r(1, 5, -1), r(3, 5, 0)], false);
});
test('Fold three levels', () => {
let range = [
'A',
' A',
' A',
' A',
'A'
];
assertRanges(range, [r(1, 4, -1), r(2, 4, 0), r(3, 4, 1)], true);
assertRanges(range, [r(1, 4, -1), r(2, 4, 0), r(3, 4, 1)], false);
});
test('Fold decreasing indent', () => {
let range = [
' A',
' A',
'A'
];
assertRanges(range, [], true);
assertRanges(range, [], false);
});
test('Fold Java', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' void foo() {',
/* 3*/ ' console.log();',
/* 4*/ ' console.log();',
/* 5*/ ' }',
/* 6*/ '',
/* 7*/ ' void bar() {',
/* 8*/ ' console.log();',
/* 9*/ ' }',
/*10*/ '}',
/*11*/ 'interface B {',
/*12*/ ' void bar();',
/*13*/ '}',
], [r(1, 9, -1), r(2, 4, 0), r(7, 8, 0), r(11, 12, -1)], false);
});
test('Fold Javadoc', () => {
assertRanges([
/* 1*/ '/**',
/* 2*/ ' * Comment',
/* 3*/ ' */',
/* 4*/ 'class A {',
/* 5*/ ' void foo() {',
/* 6*/ ' }',
/* 7*/ '}',
], [r(1, 3, -1), r(4, 6, -1)], false);
});
test('Fold Whitespace Java', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '',
/* 3*/ ' void foo() {',
/* 4*/ ' ',
/* 5*/ ' return 0;',
/* 6*/ ' }',
/* 7*/ ' ',
/* 8*/ '}',
], [r(1, 7, -1), r(3, 5, 0)], false);
});
test('Fold Whitespace Python', () => {
assertRanges([
/* 1*/ 'def a:',
/* 2*/ ' pass',
/* 3*/ ' ',
/* 4*/ ' def b:',
/* 5*/ ' pass',
/* 6*/ ' ',
/* 7*/ ' ',
/* 8*/ 'def c: # since there was a deintent here'
], [r(1, 5, -1), r(4, 5, 0)], true);
});
test('Fold Tabs', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '\t\t',
/* 3*/ '\tvoid foo() {',
/* 4*/ '\t \t//hello',
/* 5*/ '\t return 0;',
/* 6*/ ' \t}',
/* 7*/ ' ',
/* 8*/ '}',
], [r(1, 7, -1), r(3, 5, 0)], false);
});
});
let markers: FoldingMarkers = {
start: /^\s*#region\b/,
end: /^\s*#endregion\b/
};
suite('Folding with regions', () => {
test('Inside region, indented', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' #region',
/* 3*/ ' void foo() {',
/* 4*/ ' ',
/* 5*/ ' return 0;',
/* 6*/ ' }',
/* 7*/ ' #endregion',
/* 8*/ '}',
], [r(1, 7, -1), r(2, 7, 0, true), r(3, 5, 1)], false, markers);
});
test('Inside region, not indented', () => {
assertRanges([
/* 1*/ 'var x;',
/* 2*/ '#region',
/* 3*/ 'void foo() {',
/* 4*/ ' ',
/* 5*/ ' return 0;',
/* 6*/ ' }',
/* 7*/ '#endregion',
/* 8*/ '',
], [r(2, 7, -1, true), r(3, 6, 0)], false, markers);
});
test('Empty Regions', () => {
assertRanges([
/* 1*/ 'var x;',
/* 2*/ '#region',
/* 3*/ '#endregion',
/* 4*/ '#region',
/* 5*/ '',
/* 6*/ '#endregion',
/* 7*/ 'var y;',
], [r(2, 3, -1, true), r(4, 6, -1, true)], false, markers);
});
test('Nested Regions', () => {
assertRanges([
/* 1*/ 'var x;',
/* 2*/ '#region',
/* 3*/ '#region',
/* 4*/ '',
/* 5*/ '#endregion',
/* 6*/ '#endregion',
/* 7*/ 'var y;',
], [r(2, 6, -1, true), r(3, 5, 0, true)], false, markers);
});
test('Nested Regions 2', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ ' #region',
/* 3*/ '',
/* 4*/ ' #region',
/* 5*/ '',
/* 6*/ ' #endregion',
/* 7*/ ' // comment',
/* 8*/ ' #endregion',
/* 9*/ '}',
], [r(1, 8, -1), r(2, 8, 0, true), r(4, 6, 1, true)], false, markers);
});
test('Incomplete Regions', () => {
assertRanges([
/* 1*/ 'class A {',
/* 2*/ '#region',
/* 3*/ ' // comment',
/* 4*/ '}',
], [r(2, 3, -1)], false, markers);
});
test('Incomplete Regions 2', () => {
assertRanges([
/* 1*/ '',
/* 2*/ '#region',
/* 3*/ '#region',
/* 4*/ '#region',
/* 5*/ ' // comment',
/* 6*/ '#endregion',
/* 7*/ '#endregion',
/* 8*/ ' // hello',
], [r(3, 7, -1, true), r(4, 6, 0, true)], false, markers);
});
test('Indented region before', () => {
assertRanges([
/* 1*/ 'if (x)',
/* 2*/ ' return;',
/* 3*/ '',
/* 4*/ '#region',
/* 5*/ ' // comment',
/* 6*/ '#endregion',
], [r(1, 3, -1), r(4, 6, -1, true)], false, markers);
});
test('Indented region before 2', () => {
assertRanges([
/* 1*/ 'if (x)',
/* 2*/ ' log();',
/* 3*/ '',
/* 4*/ ' #region',
/* 5*/ ' // comment',
/* 6*/ ' #endregion',
], [r(1, 6, -1), r(2, 6, 0), r(4, 6, 1, true)], false, markers);
});
test('Indented region in-between', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' // comment',
/* 3*/ ' if (x)',
/* 4*/ ' return;',
/* 5*/ '',
/* 6*/ '#endregion',
], [r(1, 6, -1, true), r(3, 5, 0)], false, markers);
});
test('Indented region after', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' // comment',
/* 3*/ '',
/* 4*/ '#endregion',
/* 5*/ ' if (x)',
/* 6*/ ' return;',
], [r(1, 4, -1, true), r(5, 6, -1)], false, markers);
});
test('With off-side', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' ',
/* 3*/ '',
/* 4*/ '#endregion',
/* 5*/ '',
], [r(1, 4, -1, true)], true, markers);
});
test('Nested with off-side', () => {
assertRanges([
/* 1*/ '#region',
/* 2*/ ' ',
/* 3*/ '#region',
/* 4*/ '',
/* 5*/ '#endregion',
/* 6*/ '',
/* 7*/ '#endregion',
/* 8*/ '',
], [r(1, 7, -1, true), r(3, 5, 0, true)], true, markers);
});
test('Issue 35981', () => {
assertRanges([
/* 1*/ 'function thisFoldsToEndOfPage() {',
/* 2*/ ' const variable = []',
/* 3*/ ' // #region',
/* 4*/ ' .reduce((a, b) => a,[]);',
/* 5*/ '}',
/* 6*/ '',
/* 7*/ 'function thisFoldsProperly() {',
/* 8*/ ' const foo = "bar"',
/* 9*/ '}',
], [r(1, 4, -1), r(2, 4, 0), r(7, 8, -1)], false, markers);
});
test('Misspelled Markers', () => {
assertRanges([
/* 1*/ '#Region',
/* 2*/ '#endregion',
/* 3*/ '#regionsandmore',
/* 4*/ '#endregion',
/* 5*/ '#region',
/* 6*/ '#end region',
/* 7*/ '#region',
/* 8*/ '#endregionff',
], [], true, markers);
});
});

View File

@@ -11,7 +11,7 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { TPromise } from 'vs/base/common/winjs.base';
import { Range } from 'vs/editor/common/core/range';
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
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, sequence } from 'vs/base/common/async';
@@ -84,7 +84,7 @@ export function getOnTypeFormattingEdits(model: IReadOnlyModel, position: Positi
}).then(r => r, onUnexpectedExternalError);
}
CommonEditorRegistry.registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) {
registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) {
const { resource, range, options } = args;
if (!(resource instanceof URI) || !Range.isIRange(range)) {
throw illegalArgument();
@@ -96,7 +96,7 @@ CommonEditorRegistry.registerLanguageCommand('_executeFormatRangeProvider', func
return getDocumentRangeFormattingEdits(model, Range.lift(range), options);
});
CommonEditorRegistry.registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) {
registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) {
const { resource, options } = args;
if (!(resource instanceof URI)) {
throw illegalArgument('resource');
@@ -109,7 +109,7 @@ CommonEditorRegistry.registerLanguageCommand('_executeFormatDocumentProvider', f
return getDocumentFormattingEdits(model, options);
});
CommonEditorRegistry.registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) {
registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) {
const { ch, options } = args;
if (typeof ch !== 'string') {
throw illegalArgument('ch');

View File

@@ -11,19 +11,20 @@ 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 { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution, IActionOptions } from 'vs/editor/browser/editorExtensions';
import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, NoProviderError } from '../common/format';
import { EditOperationsCommand } from '../common/formatCommand';
import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format';
import { EditOperationsCommand } from 'vs/editor/contrib/format/formatCommand';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
import { Range } from 'vs/editor/common/core/range';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
function alertFormattingEdits(edits: editorCommon.ISingleEditOperation[]): void {
@@ -53,17 +54,16 @@ function alertFormattingEdits(edits: editorCommon.ISingleEditOperation[]): void
}
}
@commonEditorContribution
class FormatOnType implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.autoFormat';
private static readonly ID = 'editor.contrib.autoFormat';
private editor: editorCommon.ICommonCodeEditor;
private editor: ICodeEditor;
private workerService: IEditorWorkerService;
private callOnDispose: IDisposable[];
private callOnModel: IDisposable[];
constructor(editor: editorCommon.ICommonCodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) {
constructor(editor: ICodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) {
this.editor = editor;
this.workerService = workerService;
this.callOnDispose = [];
@@ -179,17 +179,16 @@ class FormatOnType implements editorCommon.IEditorContribution {
}
}
@commonEditorContribution
class FormatOnPaste implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.formatOnPaste';
private static readonly ID = 'editor.contrib.formatOnPaste';
private editor: editorCommon.ICommonCodeEditor;
private editor: ICodeEditor;
private workerService: IEditorWorkerService;
private callOnDispose: IDisposable[];
private callOnModel: IDisposable[];
constructor(editor: editorCommon.ICommonCodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) {
constructor(editor: ICodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) {
this.editor = editor;
this.workerService = workerService;
this.callOnDispose = [];
@@ -261,7 +260,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
export abstract class AbstractFormatAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const workerService = accessor.get(IEditorWorkerService);
const messageService = accessor.get(IMessageService);
@@ -295,11 +294,9 @@ export abstract class AbstractFormatAction extends EditorAction {
});
}
protected abstract _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise<editorCommon.ISingleEditOperation[]>;
protected abstract _getFormattingEdits(editor: ICodeEditor): TPromise<editorCommon.ISingleEditOperation[]>;
}
@editorAction
export class FormatDocumentAction extends AbstractFormatAction {
constructor() {
@@ -322,14 +319,13 @@ export class FormatDocumentAction extends AbstractFormatAction {
});
}
protected _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
protected _getFormattingEdits(editor: ICodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
const model = editor.getModel();
const { tabSize, insertSpaces } = model.getOptions();
return getDocumentFormattingEdits(model, { tabSize, insertSpaces });
}
}
@editorAction
export class FormatSelectionAction extends AbstractFormatAction {
constructor() {
@@ -350,13 +346,18 @@ export class FormatSelectionAction extends AbstractFormatAction {
});
}
protected _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
protected _getFormattingEdits(editor: ICodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
const model = editor.getModel();
const { tabSize, insertSpaces } = model.getOptions();
return getDocumentRangeFormattingEdits(model, editor.getSelection(), { tabSize, insertSpaces });
}
}
registerEditorContribution(FormatOnType);
registerEditorContribution(FormatOnPaste);
registerEditorAction(FormatDocumentAction);
registerEditorAction(FormatSelectionAction);
// this is the old format action that does both (format document OR format selection)
// and we keep it here such that existing keybinding configurations etc will still work
CommandsRegistry.registerCommand('editor.action.format', accessor => {
@@ -364,9 +365,9 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => {
if (editor) {
return new class extends AbstractFormatAction {
constructor() {
super(<any>{});
super({} as IActionOptions);
}
_getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
_getFormattingEdits(editor: ICodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
const model = editor.getModel();
const editorSelection = editor.getSelection();
const { tabSize, insertSpaces } = model.getOptions();

View File

@@ -9,10 +9,11 @@ import { Range } from 'vs/editor/common/core/range';
import { TextEdit } from 'vs/editor/common/modes';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Selection } from 'vs/editor/common/core/selection';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export class EditOperationsCommand implements editorCommon.ICommand {
static execute(editor: editorCommon.ICommonCodeEditor, edits: TextEdit[]) {
static execute(editor: ICodeEditor, edits: TextEdit[]) {
const cmd = new EditOperationsCommand(edits, editor.getSelection());
if (typeof cmd._newEol === 'number') {
editor.getModel().setEOL(cmd._newEol);

View File

@@ -9,8 +9,8 @@ import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ISingleEditOperation } from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
import { EditOperationsCommand } from 'vs/editor/contrib/format/common/formatCommand';
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
import { EditOperationsCommand } from 'vs/editor/contrib/format/formatCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): ISingleEditOperation {
return {
@@ -310,4 +310,4 @@ suite('FormatCommand', () => {
);
});
});
});

View File

@@ -8,7 +8,7 @@
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry';
import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location } from 'vs/editor/common/modes';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -68,6 +68,6 @@ export function getTypeDefinitionsAtPosition(model: IReadOnlyModel, position: Po
});
}
CommonEditorRegistry.registerDefaultLanguageCommand('_executeDefinitionProvider', getDefinitionsAtPosition);
CommonEditorRegistry.registerDefaultLanguageCommand('_executeImplementationProvider', getImplementationsAtPosition);
CommonEditorRegistry.registerDefaultLanguageCommand('_executeTypeDefinitionProvider', getTypeDefinitionsAtPosition);
registerDefaultLanguageCommand('_executeDefinitionProvider', getDefinitionsAtPosition);
registerDefaultLanguageCommand('_executeImplementationProvider', getImplementationsAtPosition);
registerDefaultLanguageCommand('_executeTypeDefinitionProvider', getTypeDefinitionsAtPosition);

View File

@@ -15,17 +15,18 @@ import { IEditorService } from 'vs/platform/editor/common/editor';
import { IMessageService } from 'vs/platform/message/common/message';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { editorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { Location } from 'vs/editor/common/modes';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDeclaration';
import { ReferencesController } from 'vs/editor/contrib/referenceSearch/browser/referencesController';
import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/browser/referencesModel';
import { PeekContext } from 'vs/editor/contrib/referenceSearch/browser/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 { 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';
export class DefinitionActionConfig {
@@ -48,7 +49,7 @@ export class DefinitionAction extends EditorAction {
this._configuration = configuration;
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const messageService = accessor.get(IMessageService);
const editorService = accessor.get(IEditorService);
const progressService = accessor.get(IProgressService);
@@ -125,7 +126,7 @@ export class DefinitionAction extends EditorAction {
return model.references.length > 1 && nls.localize('meta.title', " {0} definitions", model.references.length);
}
private _onResult(editorService: IEditorService, editor: editorCommon.ICommonCodeEditor, model: ReferencesModel) {
private _onResult(editorService: IEditorService, editor: ICodeEditor, model: ReferencesModel) {
const msg = model.getAriaMessage();
alert(msg);
@@ -144,20 +145,21 @@ export class DefinitionAction extends EditorAction {
}
}
private _openReference(editorService: IEditorService, reference: Location, sideBySide: boolean): TPromise<editorCommon.ICommonCodeEditor> {
private _openReference(editorService: IEditorService, reference: Location, sideBySide: boolean): TPromise<ICodeEditor> {
let { uri, range } = reference;
return editorService.openEditor({
resource: uri,
options: {
selection: Range.collapseToStart(range),
revealIfVisible: !sideBySide
revealIfVisible: !sideBySide,
revealInCenterIfOutsideViewport: true
}
}, sideBySide).then(editor => {
return editor && <editorCommon.ICommonCodeEditor>editor.getControl();
return editor && <ICodeEditor>editor.getControl();
});
}
private _openInPeek(editorService: IEditorService, target: editorCommon.ICommonCodeEditor, model: ReferencesModel) {
private _openInPeek(editorService: IEditorService, target: ICodeEditor, model: ReferencesModel) {
let controller = ReferencesController.get(target);
if (controller) {
controller.toggleWidget(target.getSelection(), TPromise.as(model), {
@@ -179,10 +181,9 @@ const goToDeclarationKb = platform.isWeb
? KeyMod.CtrlCmd | KeyCode.F12
: KeyCode.F12;
@editorAction
export class GoToDefinitionAction extends DefinitionAction {
public static ID = 'editor.action.goToDeclaration';
public static readonly ID = 'editor.action.goToDeclaration';
constructor() {
super(new DefinitionActionConfig(), {
@@ -204,10 +205,9 @@ export class GoToDefinitionAction extends DefinitionAction {
}
}
@editorAction
export class OpenDefinitionToSideAction extends DefinitionAction {
public static ID = 'editor.action.openDeclarationToTheSide';
public static readonly ID = 'editor.action.openDeclarationToTheSide';
constructor() {
super(new DefinitionActionConfig(true), {
@@ -225,7 +225,6 @@ export class OpenDefinitionToSideAction extends DefinitionAction {
}
}
@editorAction
export class PeekDefinitionAction extends DefinitionAction {
constructor() {
super(new DefinitionActionConfig(void 0, true, false), {
@@ -265,10 +264,9 @@ export class ImplementationAction extends DefinitionAction {
}
}
@editorAction
export class GoToImplementationAction extends ImplementationAction {
public static ID = 'editor.action.goToImplementation';
public static readonly ID = 'editor.action.goToImplementation';
constructor() {
super(new DefinitionActionConfig(), {
@@ -286,10 +284,9 @@ export class GoToImplementationAction extends ImplementationAction {
}
}
@editorAction
export class PeekImplementationAction extends ImplementationAction {
public static ID = 'editor.action.peekImplementation';
public static readonly ID = 'editor.action.peekImplementation';
constructor() {
super(new DefinitionActionConfig(false, true, false), {
@@ -323,10 +320,9 @@ export class TypeDefinitionAction extends DefinitionAction {
}
}
@editorAction
export class GoToTypeDefintionAction extends TypeDefinitionAction {
public static ID = 'editor.action.goToTypeDefinition';
public static readonly ID = 'editor.action.goToTypeDefinition';
constructor() {
super(new DefinitionActionConfig(), {
@@ -348,10 +344,9 @@ export class GoToTypeDefintionAction extends TypeDefinitionAction {
}
}
@editorAction
export class PeekTypeDefinitionAction extends TypeDefinitionAction {
public static ID = 'editor.action.peekTypeDefinition';
public static readonly ID = 'editor.action.peekTypeDefinition';
constructor() {
super(new DefinitionActionConfig(false, true, false), {
@@ -369,3 +364,10 @@ export class PeekTypeDefinitionAction extends TypeDefinitionAction {
}
}
registerEditorAction(GoToDefinitionAction);
registerEditorAction(OpenDefinitionToSideAction);
registerEditorAction(PeekDefinitionAction);
registerEditorAction(GoToImplementationAction);
registerEditorAction(PeekImplementationAction);
registerEditorAction(GoToTypeDefintionAction);
registerEditorAction(PeekTypeDefinitionAction);

View File

@@ -16,20 +16,19 @@ import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes';
import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { getDefinitionsAtPosition } from './goToDeclaration';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { DefinitionAction, DefinitionActionConfig } from './goToDeclarationCommands';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDeclaration/browser/clickLinkGesture';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDeclaration/clickLinkGesture';
@editorContribution
class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.gotodefinitionwithmouse';
private static readonly ID = 'editor.contrib.gotodefinitionwithmouse';
static MAX_SOURCE_PREVIEW_LINES = 8;
private editor: ICodeEditor;
@@ -102,7 +101,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
this.throttler.queue(() => {
return state.validate(this.editor)
? this.findDefinition(mouseEvent.target)
: TPromise.as<Location[]>(null);
: TPromise.wrap<Location[]>(null);
}).then(results => {
if (!results || !results.length || !state.validate(this.editor)) {
@@ -218,6 +217,8 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}
}
registerEditorContribution(GotoDefinitionWithMouseEditorContribution);
registerThemingParticipant((theme, collector) => {
let activeLinkForeground = theme.getColor(editorActiveLinkForeground);
if (activeLinkForeground) {

View File

@@ -15,14 +15,12 @@ import URI from 'vs/base/common/uri';
import * as dom from 'vs/base/browser/dom';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
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 { editorAction, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import { registerColor, oneOf } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
@@ -31,6 +29,7 @@ import { AccessibilitySupport } from 'vs/base/common/platform';
import { editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/editor/common/view/editorColorRegistry';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
class MarkerModel {
@@ -397,24 +396,13 @@ class MarkerNavigationAction extends EditorAction {
this._isNext = next;
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
const telemetryService = accessor.get(ITelemetryService);
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const controller = MarkerController.get(editor);
if (!controller) {
return;
}
let model = controller.getOrCreateModel();
/* __GDPR__
"zoneWidgetShown" : {
"mode" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"${include}": [
"${EditorTelemetryData}"
]
}
*/
telemetryService.publicLog('zoneWidgetShown', { mode: 'go to error', ...editor.getTelemetryData() });
if (model) {
if (this._isNext) {
model.next();
@@ -426,12 +414,11 @@ class MarkerNavigationAction extends EditorAction {
}
}
@editorContribution
class MarkerController implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.markerController';
private static readonly ID = 'editor.contrib.markerController';
public static get(editor: editorCommon.ICommonCodeEditor): MarkerController {
public static get(editor: ICodeEditor): MarkerController {
return editor.getContribution<MarkerController>(MarkerController.ID);
}
@@ -503,7 +490,6 @@ class MarkerController implements editorCommon.IEditorContribution {
}
}
@editorAction
class NextMarkerAction extends MarkerNavigationAction {
constructor() {
super(true, {
@@ -519,7 +505,6 @@ class NextMarkerAction extends MarkerNavigationAction {
}
}
@editorAction
class PrevMarkerAction extends MarkerNavigationAction {
constructor() {
super(false, {
@@ -535,16 +520,20 @@ class PrevMarkerAction extends MarkerNavigationAction {
}
}
registerEditorContribution(MarkerController);
registerEditorAction(NextMarkerAction);
registerEditorAction(PrevMarkerAction);
const CONTEXT_MARKERS_NAVIGATION_VISIBLE = new RawContextKey<boolean>('markersNavigationVisible', false);
const MarkerCommand = EditorCommand.bindToContribution<MarkerController>(MarkerController.get);
CommonEditorRegistry.registerEditorCommand(new MarkerCommand({
registerEditorCommand(new MarkerCommand({
id: 'closeMarkersNavigation',
precondition: CONTEXT_MARKERS_NAVIGATION_VISIBLE,
handler: x => x.closeMarkersNavigation(),
kbOpts: {
weight: CommonEditorRegistry.commandWeight(50),
weight: KeybindingsRegistry.WEIGHT.editorContrib(50),
kbExpr: EditorContextKeys.focus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]

View File

@@ -9,7 +9,7 @@ import { coalesce } from 'vs/base/common/arrays';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IReadOnlyModel } from 'vs/editor/common/editorCommon';
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes';
import { asWinJsPromise } from 'vs/base/common/async';
import { Position } from 'vs/editor/common/core/position';
@@ -38,4 +38,4 @@ export function getHover(model: IReadOnlyModel, position: Position): TPromise<Ho
return TPromise.join(promises).then(() => coalesce(values));
}
CommonEditorRegistry.registerDefaultLanguageCommand('_executeHoverProvider', getHover);
registerDefaultLanguageCommand('_executeHoverProvider', getHover);

View File

@@ -27,14 +27,6 @@
max-width: 500px;
}
/*
* https://github.com/Microsoft/monaco-editor/issues/417
* Safari 10.1, fails inherit correct visibility from parent when we change the visibility of parent element from hidden to inherit, in this particular case.
*/
.monaco-editor-hover .monaco-scrollable-element {
visibility: visible;
}
.monaco-editor-hover .hover-row {
padding: 4px 5px;
}
@@ -74,4 +66,4 @@
.monaco-editor-hover .monaco-tokenized-source {
white-space: pre-wrap;
word-break: break-all;
}
}

View File

@@ -14,21 +14,19 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { ModesContentHoverWidget } from './modesContentHover';
import { ModesGlyphHoverWidget } from './modesGlyphHover';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { registerThemingParticipant } 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/browser/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
@editorContribution
export class ModesHoverController implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.hover';
private static readonly ID = 'editor.contrib.hover';
private _editor: ICodeEditor;
private _toUnhook: IDisposable[];
@@ -36,16 +34,30 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
private _contentWidget: ModesContentHoverWidget;
private _glyphWidget: ModesGlyphHoverWidget;
get contentWidget(): ModesContentHoverWidget {
if (!this._contentWidget) {
this._createHoverWidget();
}
return this._contentWidget;
}
get glyphWidget(): ModesGlyphHoverWidget {
if (!this._glyphWidget) {
this._createHoverWidget();
}
return this._glyphWidget;
}
private _isMouseDown: boolean;
private _hoverClicked: boolean;
static get(editor: editorCommon.ICommonCodeEditor): ModesHoverController {
static get(editor: ICodeEditor): ModesHoverController {
return editor.getContribution<ModesHoverController>(ModesHoverController.ID);
}
constructor(editor: ICodeEditor,
@IOpenerService openerService: IOpenerService,
@IModeService modeService: IModeService
@IOpenerService private _openerService: IOpenerService,
@IModeService private _modeService: IModeService
) {
this._editor = editor;
@@ -65,15 +77,12 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
this._hideWidgets();
}
}));
const renderer = new MarkdownRenderer(editor, modeService, openerService);
this._contentWidget = new ModesContentHoverWidget(editor, renderer);
this._glyphWidget = new ModesGlyphHoverWidget(editor, renderer);
}
}
private _onModelDecorationsChanged(): void {
this._contentWidget.onModelDecorationsChanged();
this._glyphWidget.onModelDecorationsChanged();
this.contentWidget.onModelDecorationsChanged();
this.glyphWidget.onModelDecorationsChanged();
}
private _onEditorMouseDown(mouseEvent: IEditorMouseEvent): void {
@@ -107,7 +116,7 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
var targetType = mouseEvent.target.type;
var stopKey = platform.isMacintosh ? 'metaKey' : 'ctrlKey';
if (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible()) {
if (this._isMouseDown && this._hoverClicked && this.contentWidget.isColorPickerVisible()) {
return;
}
@@ -122,11 +131,11 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
}
if (this._editor.getConfiguration().contribInfo.hover && targetType === MouseTargetType.CONTENT_TEXT) {
this._glyphWidget.hide();
this._contentWidget.startShowingAt(mouseEvent.target.range, false);
this.glyphWidget.hide();
this.contentWidget.startShowingAt(mouseEvent.target.range, false);
} else if (targetType === MouseTargetType.GUTTER_GLYPH_MARGIN) {
this._contentWidget.hide();
this._glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
this.contentWidget.hide();
this.glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
} else {
this._hideWidgets();
}
@@ -140,7 +149,7 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
}
private _hideWidgets(): void {
if (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible()) {
if (!this._contentWidget || (this._isMouseDown && this._hoverClicked && this._contentWidget.isColorPickerVisible())) {
return;
}
@@ -148,8 +157,14 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
this._contentWidget.hide();
}
private _createHoverWidget() {
const renderer = new MarkdownRenderer(this._editor, this._modeService, this._openerService);
this._contentWidget = new ModesContentHoverWidget(this._editor, renderer);
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, renderer);
}
public showContentHover(range: Range, focus: boolean): void {
this._contentWidget.startShowingAt(range, focus);
this.contentWidget.startShowingAt(range, focus);
}
public getId(): string {
@@ -169,7 +184,6 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
}
}
@editorAction
class ShowHoverAction extends EditorAction {
constructor() {
@@ -185,7 +199,7 @@ class ShowHoverAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let controller = ModesHoverController.get(editor);
if (!controller) {
return;
@@ -196,6 +210,9 @@ class ShowHoverAction extends EditorAction {
}
}
registerEditorContribution(ModesHoverController);
registerEditorAction(ShowHoverAction);
// theming
registerThemingParticipant((theme, collector) => {
let editorHoverHighlightColor = theme.getColor(editorHoverHighlight);

View File

@@ -79,10 +79,6 @@ export class HoverOperation<Result> {
this._progressCallback = progress;
}
public getComputer(): IHoverComputer<Result> {
return this._computer;
}
private _getHoverTimeMillis(): number {
if (this._computer.getHoverTimeMillis) {
return this._computer.getHoverTimeMillis();
@@ -186,4 +182,3 @@ export class HoverOperation<Result> {
}
}

View File

@@ -11,18 +11,18 @@ 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 '../common/hover';
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { HoverOperation, IHoverComputer } from './hoverOperation';
import { ContentHoverWidget } from './hoverWidgets';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/browser/colorPickerModel';
import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/browser/colorPickerWidget';
import { ColorDetector } from 'vs/editor/contrib/colorPicker/browser/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, empty as EmptyDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { getColorPresentations } from 'vs/editor/contrib/colorPicker/common/color';
import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color';
const $ = dom.$;
class ColorHover {
@@ -295,7 +295,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
// update column from which to show
var renderColumn = Number.MAX_VALUE,
highlightRange = messages[0].range,
fragment = document.createDocumentFragment();
fragment = document.createDocumentFragment(),
isEmptyHoverContent = true;
let containColorPicker = false;
messages.forEach((msg) => {
@@ -312,6 +313,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
.forEach(contents => {
const renderedContents = this._markdownRenderer.render(contents);
fragment.appendChild($('div.hover-row', null, renderedContents));
isEmptyHoverContent = false;
});
} else {
containColorPicker = true;
@@ -392,7 +394,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
// show
if (!containColorPicker) {
if (!containColorPicker && !isEmptyHoverContent) {
this.showAt(new Position(renderRange.startLineNumber, renderColumn), this._shouldFocus);
this.updateContents(fragment);
}
@@ -405,7 +407,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._isChangingDecorations = false;
}
private static _DECORATION_OPTIONS = ModelDecorationOptions.register({
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
className: 'hoverHighlight'
});
}

View File

@@ -8,7 +8,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { HoverOperation, IHoverComputer } from './hoverOperation';
import { GlyphHoverWidget } from './hoverWidgets';
import { $ } from 'vs/base/browser/dom';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IMarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent';
export interface IHoverMessage {
@@ -84,7 +84,7 @@ class MarginComputer implements IHoverComputer<IHoverMessage[]> {
export class ModesGlyphHoverWidget extends GlyphHoverWidget {
public static ID = 'editor.contrib.modesGlyphHoverWidget';
public static readonly ID = 'editor.contrib.modesGlyphHoverWidget';
private _messages: IHoverMessage[];
private _lastLineNumber: number;

View File

@@ -9,44 +9,42 @@ 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, ICommonCodeEditor, IModelDecorationsChangeAccessor } from 'vs/editor/common/editorCommon';
import { IEditorContribution, IModelDecorationsChangeAccessor } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IInplaceReplaceSupportResult } from 'vs/editor/common/modes';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { InPlaceReplaceCommand } from './inPlaceReplaceCommand';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorBracketMatchBorder } from 'vs/editor/common/view/editorColorRegistry';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@commonEditorContribution
class InPlaceReplaceController implements IEditorContribution {
private static ID = 'editor.contrib.inPlaceReplaceController';
private static readonly ID = 'editor.contrib.inPlaceReplaceController';
static get(editor: ICommonCodeEditor): InPlaceReplaceController {
static get(editor: ICodeEditor): InPlaceReplaceController {
return editor.getContribution<InPlaceReplaceController>(InPlaceReplaceController.ID);
}
private static DECORATION = ModelDecorationOptions.register({
private static readonly DECORATION = ModelDecorationOptions.register({
className: 'valueSetReplacement'
});
private editor: ICommonCodeEditor;
private requestIdPool: number;
private editor: ICodeEditor;
private currentRequest: TPromise<IInplaceReplaceSupportResult>;
private decorationRemover: TPromise<void>;
private decorationIds: string[];
private editorWorkerService: IEditorWorkerService;
constructor(
editor: ICommonCodeEditor,
editor: ICodeEditor,
@IEditorWorkerService editorWorkerService: IEditorWorkerService
) {
this.editor = editor;
this.editorWorkerService = editorWorkerService;
this.requestIdPool = 0;
this.currentRequest = TPromise.as(<IInplaceReplaceSupportResult>null);
this.decorationRemover = TPromise.as(<void>null);
this.decorationIds = [];
@@ -140,7 +138,6 @@ class InPlaceReplaceController implements IEditorContribution {
}
}
@editorAction
class InPlaceReplaceUp extends EditorAction {
constructor() {
@@ -156,7 +153,7 @@ class InPlaceReplaceUp extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
let controller = InPlaceReplaceController.get(editor);
if (!controller) {
return undefined;
@@ -165,7 +162,6 @@ class InPlaceReplaceUp extends EditorAction {
}
}
@editorAction
class InPlaceReplaceDown extends EditorAction {
constructor() {
@@ -181,7 +177,7 @@ class InPlaceReplaceDown extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
let controller = InPlaceReplaceController.get(editor);
if (!controller) {
return undefined;
@@ -190,9 +186,13 @@ class InPlaceReplaceDown extends EditorAction {
}
}
registerEditorContribution(InPlaceReplaceController);
registerEditorAction(InPlaceReplaceUp);
registerEditorAction(InPlaceReplaceDown);
registerThemingParticipant((theme, collector) => {
let border = theme.getColor(editorBracketMatchBorder);
if (border) {
collector.addRule(`.monaco-editor.vs .valueSetReplacement { outline: solid 2px ${border}; }`);
}
});
});

View File

@@ -7,9 +7,9 @@ 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 { ICommonCodeEditor, IEditorContribution, IIdentifiedSingleEditOperation, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel, EndOfLineSequence } from 'vs/editor/common/editorCommon';
import { IEditorContribution, IIdentifiedSingleEditOperation, ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { editorAction, ServicesAccessor, IActionOptions, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
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';
@@ -20,6 +20,8 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
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 { IndentConsts } from 'vs/editor/common/modes/supports/indentRules';
export function shiftIndent(tabSize: number, indentation: string, count?: number): string {
count = count || 1;
@@ -147,9 +149,8 @@ export function getReindentEditOperations(model: ITokenizedModel, startLineNumbe
return indentEdits;
}
@editorAction
export class IndentationToSpacesAction extends EditorAction {
public static ID = 'editor.action.indentationToSpaces';
public static readonly ID = 'editor.action.indentationToSpaces';
constructor() {
super({
@@ -160,7 +161,7 @@ export class IndentationToSpacesAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let model = editor.getModel();
if (!model) {
return;
@@ -178,9 +179,8 @@ export class IndentationToSpacesAction extends EditorAction {
}
}
@editorAction
export class IndentationToTabsAction extends EditorAction {
public static ID = 'editor.action.indentationToTabs';
public static readonly ID = 'editor.action.indentationToTabs';
constructor() {
super({
@@ -191,7 +191,7 @@ export class IndentationToTabsAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let model = editor.getModel();
if (!model) {
return;
@@ -215,7 +215,7 @@ export class ChangeIndentationSizeAction extends EditorAction {
super(opts);
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): TPromise<void> {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const quickOpenService = accessor.get(IQuickOpenService);
const modelService = accessor.get(IModelService);
@@ -248,10 +248,9 @@ export class ChangeIndentationSizeAction extends EditorAction {
}
}
@editorAction
export class IndentUsingTabs extends ChangeIndentationSizeAction {
public static ID = 'editor.action.indentUsingTabs';
public static readonly ID = 'editor.action.indentUsingTabs';
constructor() {
super(false, {
@@ -263,10 +262,9 @@ export class IndentUsingTabs extends ChangeIndentationSizeAction {
}
}
@editorAction
export class IndentUsingSpaces extends ChangeIndentationSizeAction {
public static ID = 'editor.action.indentUsingSpaces';
public static readonly ID = 'editor.action.indentUsingSpaces';
constructor() {
super(true, {
@@ -278,10 +276,9 @@ export class IndentUsingSpaces extends ChangeIndentationSizeAction {
}
}
@editorAction
export class DetectIndentation extends EditorAction {
public static ID = 'editor.action.detectIndentation';
public static readonly ID = 'editor.action.detectIndentation';
constructor() {
super({
@@ -292,7 +289,7 @@ export class DetectIndentation extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const modelService = accessor.get(IModelService);
let model = editor.getModel();
@@ -305,7 +302,6 @@ export class DetectIndentation extends EditorAction {
}
}
@editorAction
export class ReindentLinesAction extends EditorAction {
constructor() {
super({
@@ -316,7 +312,7 @@ export class ReindentLinesAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
let model = editor.getModel();
if (!model) {
return;
@@ -333,7 +329,6 @@ export class ReindentLinesAction extends EditorAction {
export class AutoIndentOnPasteCommand implements ICommand {
private _edits: TextEdit[];
private _newEol: EndOfLineSequence;
private _initialSelection: Selection;
private _selectionId: string;
@@ -341,12 +336,8 @@ export class AutoIndentOnPasteCommand implements ICommand {
constructor(edits: TextEdit[], initialSelection: Selection) {
this._initialSelection = initialSelection;
this._edits = [];
this._newEol = undefined;
for (let edit of edits) {
if (typeof edit.eol === 'number') {
this._newEol = edit.eol;
}
if (edit.range && typeof edit.text === 'string') {
this._edits.push(edit);
}
@@ -381,15 +372,14 @@ export class AutoIndentOnPasteCommand implements ICommand {
}
}
@commonEditorContribution
export class AutoIndentOnPaste implements IEditorContribution {
private static ID = 'editor.contrib.autoIndentOnPaste';
private static readonly ID = 'editor.contrib.autoIndentOnPaste';
private editor: ICommonCodeEditor;
private editor: ICodeEditor;
private callOnDispose: IDisposable[];
private callOnModel: IDisposable[];
constructor(editor: ICommonCodeEditor) {
constructor(editor: ICodeEditor) {
this.editor = editor;
this.callOnDispose = [];
this.callOnModel = [];
@@ -483,10 +473,31 @@ export class AutoIndentOnPaste implements IEditorContribution {
text: newIndent
});
firstLineText = newIndent + firstLineText.substr(oldIndentation.length);
} else {
let indentMetadata = LanguageConfigurationRegistry.getIndentMetadata(model, startLineNumber);
if (indentMetadata === 0 || indentMetadata === IndentConsts.UNINDENT_MASK) {
// we paste content into a line where only contains whitespaces
// after pasting, the indentation of the first line is already correct
// the first line doesn't match any indentation rule
// then no-op.
return;
}
}
}
}
const firstLineNumber = startLineNumber;
// ignore empty or ignored lines
while (startLineNumber < range.endLineNumber) {
if (!/\S/.test(model.getLineContent(startLineNumber + 1))) {
startLineNumber++;
continue;
}
break;
}
if (startLineNumber !== range.endLineNumber) {
let virtualModel = {
getLineTokens: (lineNumber: number) => {
@@ -499,7 +510,7 @@ export class AutoIndentOnPaste implements IEditorContribution {
return model.getLanguageIdAtPosition(lineNumber, column);
},
getLineContent: (lineNumber: number) => {
if (lineNumber === startLineNumber) {
if (lineNumber === firstLineNumber) {
return firstLineText;
} else {
return model.getLineContent(lineNumber);
@@ -620,3 +631,11 @@ export class IndentationToTabsCommand implements ICommand {
return helper.getTrackedSelection(this.selectionId);
}
}
registerEditorContribution(AutoIndentOnPaste);
registerEditorAction(IndentationToSpacesAction);
registerEditorAction(IndentationToTabsAction);
registerEditorAction(IndentUsingTabs);
registerEditorAction(IndentUsingSpaces);
registerEditorAction(DetectIndentation);
registerEditorAction(ReindentLinesAction);

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