提交 83b7762e 编写于 作者: A Alex Dima

Fixes #1171: Change All Occurences gets the same implementation as Select All...

Fixes #1171: Change All Occurences gets the same implementation as Select All Occurences of Find Match
上级 0579a68a
......@@ -26,7 +26,6 @@ define([
'vs/editor/contrib/parameterHints/browser/parameterHints',
'vs/editor/contrib/quickFix/browser/quickFix',
'vs/editor/contrib/referenceSearch/browser/referenceSearch',
'vs/editor/contrib/rename/browser/rename',
'vs/editor/contrib/rename/browser/rename2',
'vs/editor/contrib/smartSelect/common/smartSelect',
'vs/editor/contrib/smartSelect/common/jumpToBracket',
......
......@@ -35,7 +35,6 @@ export class AbstractMode<W extends AbstractModeWorker> implements Modes.IMode {
// adapters start
public autoValidateDelay:number;
public occurrencesSupport:Modes.IOccurrencesSupport;
public suggestSupport:Modes.ISuggestSupport;
public inplaceReplaceSupport:Modes.IInplaceReplaceSupport;
public diffSupport:Modes.IDiffSupport;
......@@ -63,7 +62,6 @@ export class AbstractMode<W extends AbstractModeWorker> implements Modes.IMode {
this._options = null;
this.autoValidateDelay = 500;
this.occurrencesSupport = this;
this.suggestSupport = this;
this.inplaceReplaceSupport = this;
this.diffSupport = this;
......@@ -194,11 +192,6 @@ export class AbstractMode<W extends AbstractModeWorker> implements Modes.IMode {
return true;
}
static $findOccurrences = OneWorkerAttr(AbstractMode, AbstractMode.prototype.findOccurrences);
public findOccurrences(resource:URI, position:EditorCommon.IPosition, strict:boolean = false): TPromise<Modes.IOccurence[]> {
return this._worker((w) => w.findOccurrences(resource, position, strict));
}
static $navigateValueSet = OneWorkerAttr(AbstractMode, AbstractMode.prototype.navigateValueSet);
public navigateValueSet(resource:URI, position:EditorCommon.IRange, up:boolean):TPromise<Modes.IInplaceReplaceSupportResult> {
return this._worker((w) => w.inplaceReplaceSupport.navigateValueSet(resource, position, up));
......
......@@ -177,30 +177,6 @@ export class AbstractModeWorker {
return AbstractModeWorker.filter;
}
// ---- occurrences ---------------------------------------------------------------
public findOccurrences(resource:URI, position:EditorCommon.IPosition, strict?:boolean):TPromise<Modes.IOccurence[]> {
var model = this.resourceService.get(resource),
wordAtPosition = model.getWordAtPosition(position),
currentWord = (wordAtPosition ? wordAtPosition.word : ''),
result:Modes.IOccurence[] = [];
var words = model.getAllWordsWithRange(),
upperBound = Math.min(1000, words.length); // Limit find occurences to 1000 occurences
for(var i = 0; i < upperBound; i++) {
if(words[i].text === currentWord) {
result.push({
range: words[i].range,
kind: 'text'
});
}
}
return TPromise.as(result);
}
// ---- diff --------------------------------------------------------------------------
public computeDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise<EditorCommon.ILineChange[]> {
......
......@@ -22,6 +22,7 @@ import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
import {INullService} from 'vs/platform/instantiation/common/instantiation';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {Range} from 'vs/editor/common/core/range';
import {OccurrencesRegistry} from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter';
/**
* The Find controller will survive an editor.setModel(..) call
......@@ -459,9 +460,18 @@ class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
class SelectHighlightsAction extends EditorAction {
static ID = 'editor.action.selectHighlights';
static COMPAT_ID = 'editor.action.changeAll';
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
let behaviour = Behaviour.WidgetFocus;
if (descriptor.id === SelectHighlightsAction.COMPAT_ID) {
behaviour |= Behaviour.ShowInContextMenu;
}
super(descriptor, editor, behaviour);
}
public getGroupId(): string {
return '2_change/1_changeAll';
}
public run(): TPromise<boolean> {
......@@ -484,20 +494,17 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
static ID = 'editor.contrib.selectionHighlighter';
private editor:EditorCommon.ICommonCodeEditor;
private model:EditorCommon.IModel;
private decorations:string[];
private toDispose:Lifecycle.IDisposable[];
constructor(editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
this.editor = editor;
this.model = this.editor.getModel();
this.decorations = [];
this.toDispose = [];
this.toDispose.push(editor.addListener2(EditorCommon.EventType.CursorPositionChanged, _ => this._update()));
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
this.removeDecorations();
this.model = this.editor.getModel();
}));
this.toDispose.push(FindController.getFindController(editor).onStateChanged(() => this._update()));
}
......@@ -522,10 +529,15 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
this.removeDecorations();
return;
}
let model = this.editor.getModel();
if (r.nextMatch) {
// This is an empty selection
this.removeDecorations();
return;
if (OccurrencesRegistry.has(model)) {
// Do not interfere with semantic word highlighting in the no selection case
this.removeDecorations();
return;
}
}
if (/^[ \t]+$/.test(r.searchText)) {
// whitespace only selection
......@@ -538,7 +550,7 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
return;
}
let allMatches = this.editor.getModel().findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
let allMatches = model.findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
allMatches.sort(Range.compareRangesUsingStarts);
let selections = this.editor.getSelections();
......@@ -594,6 +606,11 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighl
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L
}));
// register SelectHighlightsAction again to replace the now removed Change All action
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighlightsAction, SelectHighlightsAction.COMPAT_ID, nls.localize('changeAll.label', "Change All Occurrences"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F2
}));
var CONTEXT_FIND_WIDGET_VISIBLE = 'findWidgetVisible';
......
......@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-editor .linked-editing-placeholder {
}
.monaco-editor .word-level-progress {
font-style: italic;
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./rename';
import {TPromise} from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
import {EditorAction, Behaviour} from 'vs/editor/common/editorAction';
import EditorCommon = require('vs/editor/common/editorCommon');
import Modes = require('vs/editor/common/modes');
import Severity from 'vs/base/common/severity';
import EventEmitter = require('vs/base/common/eventEmitter');
import {Range} from 'vs/editor/common/core/range';
import {IMessageService} from 'vs/platform/message/common/message';
import {IProgressService, IProgressRunner} from 'vs/platform/progress/common/progress';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
class LinkedEditingController {
private editor: EditorCommon.ICommonCodeEditor;
private listenersToRemove:EventEmitter.ListenerUnbind[];
private decorations:string[];
private isDisposed: boolean;
private _onDispose: () => void;
constructor(editor: EditorCommon.ICommonCodeEditor, selections: EditorCommon.ISelection[], ranges: EditorCommon.IRange[], onDispose: () => void) {
this._onDispose = onDispose;
this.editor = editor;
this.isDisposed = false;
// Decorate editing ranges
this.editor.changeDecorations((changeAccessor:EditorCommon.IModelDecorationsChangeAccessor) => {
var newDecorations: EditorCommon.IModelDeltaDecoration[] = [];
for (var i = 0, len = selections.length; i < len; i++) {
var className = 'linked-editing-placeholder';
newDecorations.push({
range: ranges[i],
options: {
className: className
}
});
}
this.decorations = changeAccessor.deltaDecorations([], newDecorations);
});
// Begin linked editing (multi-cursor)
this.editor.setSelections(selections);
this.listenersToRemove = [];
this.listenersToRemove.push(this.editor.addListener(EditorCommon.EventType.CursorPositionChanged, (e:EditorCommon.ICursorPositionChangedEvent) => {
if (this.isDisposed) {
return;
}
var cursorCount = 1 + e.secondaryPositions.length;
if (cursorCount !== this.decorations.length) {
this.dispose();
}
}));
}
public onEnterOrEscape(): boolean {
if (this.isDisposed) {
return;
}
// Basically cancel multi-cursor
this.editor.setSelection(this.editor.getSelection());
this.dispose();
return true;
}
public dispose(): void {
if (this.isDisposed) {
return;
}
this.isDisposed = true;
this._onDispose();
this.decorations = this.editor.deltaDecorations(this.decorations, []);
this.listenersToRemove.forEach((element) => {
element();
});
this.listenersToRemove = [];
}
}
class LocalProgressService implements IProgressService {
public serviceId = IProgressService;
constructor(private _editor:EditorCommon.ICommonCodeEditor) {
//
}
showWhile<T>(promise:TPromise<T>, delay?:number):TPromise<T> {
var decoration: string,
delayHandle: number;
delayHandle = setTimeout(() => {
decoration = this._addDecoration();
}, delay || 0);
return promise.then((value) => {
clearTimeout(delayHandle);
this._removeDecoration(decoration);
return value;
}, (err) => {
clearTimeout(delayHandle);
this._removeDecoration(decoration);
throw err;
});
}
private _addDecoration():string {
var position = this._editor.getPosition(),
word = this._editor.getModel().getWordAtPosition(position),
decorationId:string;
var decorations = this._editor.deltaDecorations([], [{
range: {
startLineNumber: position.lineNumber,
startColumn: word.startColumn,
endLineNumber: position.lineNumber,
endColumn: word.endColumn
},
options: {
inlineClassName: 'word-level-progress'
}
}]);
return decorations[0];
}
private _removeDecoration(decorationId:string):void {
if(decorationId) {
this._editor.changeDecorations((accessor) => {
accessor.deltaDecorations([decorationId], []);
});
}
}
public show(...args:any[]):IProgressRunner {
throw new Error('not implemented');
}
}
export class ChangeAllAction extends EditorAction {
public static ID = 'editor.action.changeAll';
private _idPool:number;
private _messageService:IMessageService;
private _progressService: IProgressService;
private _currentController: LinkedEditingController;
private _changeAllMode: IKeybindingContextKey<boolean>;
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @IMessageService messageService: IMessageService, @IKeybindingService keybindingService: IKeybindingService) {
super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.Writeable | Behaviour.ShowInContextMenu | Behaviour.UpdateOnCursorPositionChange);
this._idPool = 0;
this._messageService = messageService;
this._progressService = new LocalProgressService(this.editor);
this._currentController = null;
this._changeAllMode = keybindingService.createKey(CONTEXT_CHANGE_ALL_MODE, false);
}
public getGroupId(): string {
return '2_change/1_changeAll';
}
public isSupported():boolean {
var mode = this.editor.getModel().getMode();
return !!mode && !!mode.occurrencesSupport && super.isSupported();
}
public computeInfos(editor:EditorCommon.ICommonCodeEditor):TPromise<Modes.IOccurence[]> {
var selection = editor.getSelection();
var position = selection.getStartPosition();
var model = editor.getModel();
return this.editor.getModel().getMode().occurrencesSupport.findOccurrences(model.getAssociatedResource(), position);
}
public run():TPromise<boolean> {
var myId = ++this._idPool,
state = this.editor.captureState(EditorCommon.CodeEditorStateFlag.Position, EditorCommon.CodeEditorStateFlag.Value),
capturedSelection = this.editor.getSelection(),
infoPromise = this.computeInfos(this.editor);
if(this._progressService) {
this._progressService.showWhile(infoPromise, 500);
}
return infoPromise.then((infos:Modes.IOccurence[]) => {
if(myId !== this._idPool) {
return;
}
if(!state.validate(this.editor)) {
return;
}
if(infos.length === 0) {
return;
}
var ranges = infos.map((info) => {
return info.range;
});
this._beginLinkedEditing(ranges, capturedSelection);
return true;
}, (e) => {
this._messageService.show(Severity.Info, e);
});
}
private _indexOf(ranges:EditorCommon.IRange[], lineNumber: number, column: number): number {
var pos = {
lineNumber: lineNumber,
column: column
};
for (var i = 0; i < ranges.length; i++) {
if (ranges[i].startLineNumber !== lineNumber) {
// Only consider ranges that start on the same line as position
continue;
}
if (Range.containsPosition(ranges[i], pos)) {
return i;
}
}
return -1;
}
private _beginLinkedEditing(ranges: EditorCommon.IRange[], capturedSelection: EditorCommon.IEditorSelection): void {
if (this._currentController) {
this._currentController.dispose();
this._currentController = null;
}
var editorSelection = this.editor.getSelection();
// Try to find a suitable range for the current editor position
var foundRangeIndex = this._indexOf(ranges, editorSelection.positionLineNumber, editorSelection.positionColumn);
if (foundRangeIndex === -1) {
// Current editor position is outside of one of these ranges, try again with the original editor position
editorSelection = capturedSelection;
foundRangeIndex = this._indexOf(ranges, editorSelection.positionLineNumber, editorSelection.positionColumn);
if (foundRangeIndex === -1) {
// These ranges are bogus!
return;
}
}
var hasSelectionInFoundRange = false;
if (!editorSelection.isEmpty()) {
if (Range.containsPosition(ranges[foundRangeIndex], { lineNumber: editorSelection.selectionStartLineNumber, column: editorSelection.selectionStartColumn})) {
hasSelectionInFoundRange = true;
}
}
var deltaColumnForPosition: number, deltaColumnForStartSelection: number;
if (hasSelectionInFoundRange) {
deltaColumnForPosition = editorSelection.positionColumn - ranges[foundRangeIndex].startColumn;
deltaColumnForStartSelection = editorSelection.selectionStartColumn - ranges[foundRangeIndex].startColumn;
} else {
deltaColumnForPosition = ranges[foundRangeIndex].endColumn - ranges[foundRangeIndex].startColumn;
deltaColumnForStartSelection = 0;
}
var newEditorSelections: EditorCommon.ISelection[] = [];
newEditorSelections.push({
selectionStartLineNumber: ranges[foundRangeIndex].startLineNumber,
selectionStartColumn: ranges[foundRangeIndex].startColumn + deltaColumnForStartSelection,
positionLineNumber: ranges[foundRangeIndex].startLineNumber,
positionColumn: ranges[foundRangeIndex].startColumn + deltaColumnForPosition,
});
for (var i = 0; i < ranges.length; i++) {
if (i !== foundRangeIndex) {
newEditorSelections.push({
selectionStartLineNumber: ranges[i].startLineNumber,
selectionStartColumn: ranges[i].startColumn + deltaColumnForStartSelection,
positionLineNumber: ranges[i].startLineNumber,
positionColumn: ranges[i].startColumn + deltaColumnForPosition,
});
}
}
this._changeAllMode.set(true);
this._currentController = new LinkedEditingController(this.editor, newEditorSelections, ranges, () => {
this._changeAllMode.reset();
});
}
public leaveChangeAllMode(): void {
if (this._currentController) {
this._currentController.onEnterOrEscape();
this._currentController = null;
}
}
}
var CONTEXT_CHANGE_ALL_MODE = 'inChangeAllMode';
var weight = CommonEditorRegistry.commandWeight(30);
// register actions
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(ChangeAllAction, ChangeAllAction.ID, nls.localize('changeAll.label', "Change All Occurrences"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F2
}));
CommonEditorRegistry.registerEditorCommand('leaveChangeAllMode', weight, { primary: KeyCode.Enter, secondary: [KeyCode.Escape] }, true, CONTEXT_CHANGE_ALL_MODE,(ctx, editor, args) => {
var action = <ChangeAllAction>editor.getAction(ChangeAllAction.ID);
action.leaveChangeAllMode();
});
\ No newline at end of file
......@@ -285,6 +285,7 @@ export class CSSMode extends AbstractMode<cssWorker.CSSWorker> {
public referenceSupport: Modes.IReferenceSupport;
public logicalSelectionSupport: Modes.ILogicalSelectionSupport;
public extraInfoSupport:Modes.IExtraInfoSupport;
public occurrencesSupport:Modes.IOccurrencesSupport;
public outlineSupport: Modes.IOutlineSupport;
public declarationSupport: Modes.IDeclarationSupport;
public suggestSupport: Modes.ISuggestSupport;
......@@ -305,6 +306,7 @@ export class CSSMode extends AbstractMode<cssWorker.CSSWorker> {
{ tokenType: 'punctuation.bracket.css', open: '{', close: '}', isElectric: true }
] });
this.occurrencesSupport = this;
this.extraInfoSupport = this;
this.referenceSupport = new supports.ReferenceSupport(this, {
tokens: [cssTokenTypes.TOKEN_PROPERTY + '.css', cssTokenTypes.TOKEN_VALUE + '.css', cssTokenTypes.TOKEN_SELECTOR_TAG + '.css'],
......@@ -344,6 +346,11 @@ export class CSSMode extends AbstractMode<cssWorker.CSSWorker> {
return createAsyncDescriptor2('vs/languages/css/common/cssWorker', 'CSSWorker');
}
static $findOccurrences = OneWorkerAttr(CSSMode, CSSMode.prototype.findOccurrences);
public findOccurrences(resource:URI, position:EditorCommon.IPosition, strict:boolean = false): WinJS.TPromise<Modes.IOccurence[]> {
return this._worker((w) => w.findOccurrences(resource, position, strict));
}
static $findDeclaration = OneWorkerAttr(CSSMode, CSSMode.prototype.findDeclaration);
public findDeclaration(resource:URI, position:EditorCommon.IPosition):WinJS.TPromise<Modes.IReference> {
return this._worker((w) => w.findDeclaration(resource, position));
......
......@@ -277,6 +277,7 @@ export class HTMLMode<W extends htmlWorker.HTMLWorker> extends AbstractMode<W> i
public characterPairSupport: Modes.ICharacterPairSupport;
public extraInfoSupport:Modes.IExtraInfoSupport;
public occurrencesSupport:Modes.IOccurrencesSupport;
public referenceSupport: Modes.IReferenceSupport;
public logicalSelectionSupport: Modes.ILogicalSelectionSupport;
public formattingSupport: Modes.IFormattingSupport;
......@@ -311,6 +312,7 @@ export class HTMLMode<W extends htmlWorker.HTMLWorker> extends AbstractMode<W> i
this.formattingSupport = this;
this.extraInfoSupport = this;
this.occurrencesSupport = this;
this.referenceSupport = new supports.ReferenceSupport(this, {
tokens: ['invalid'],
findReferences: (resource, position, includeDeclaration) => this.findReferences(resource, position, includeDeclaration)});
......
......@@ -132,6 +132,7 @@ export class TypeScriptMode<W extends typescriptWorker.TypeScriptWorker2> extend
public characterPairSupport: Modes.ICharacterPairSupport;
public referenceSupport: Modes.IReferenceSupport;
public extraInfoSupport:Modes.IExtraInfoSupport;
public occurrencesSupport:Modes.IOccurrencesSupport;
public quickFixSupport:Modes.IQuickFixSupport;
public logicalSelectionSupport:Modes.ILogicalSelectionSupport;
public parameterHintsSupport:Modes.IParameterHintsSupport;
......@@ -185,6 +186,7 @@ export class TypeScriptMode<W extends typescriptWorker.TypeScriptWorker2> extend
}
this.extraInfoSupport = this;
this.occurrencesSupport = this;
this.formattingSupport = this;
this.quickFixSupport = this;
this.logicalSelectionSupport = this;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册