未验证 提交 9c6f4c19 编写于 作者: R rebornix

notebook find with match count.

上级 5c7d17bf
......@@ -66,8 +66,8 @@ const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All");
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first {0} results are highlighted, but all find operations work on the entire text.", MATCHES_LIMIT);
const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}");
const NLS_NO_RESULTS = nls.localize('label.noResults', "No results");
export const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}");
export const NLS_NO_RESULTS = nls.localize('label.noResults', "No results");
const FIND_WIDGET_INITIAL_WIDTH = 419;
const PART_WIDTH = 275;
......
......@@ -3,25 +3,25 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./simpleFindReplaceWidget';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { IReplaceInputStyles, ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { Widget } from 'vs/base/browser/ui/widget';
import { Delayer } from 'vs/base/common/async';
import { KeyCode } from 'vs/base/common/keyCodes';
import 'vs/css!./simpleFindReplaceWidget';
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { SimpleButton, findNextMatchIcon, findPreviousMatchIcon, findReplaceIcon, findReplaceAllIcon } from 'vs/editor/contrib/find/findWidget';
import { findNextMatchIcon, findPreviousMatchIcon, findReplaceAllIcon, findReplaceIcon, SimpleButton } from 'vs/editor/contrib/find/findWidget';
import * as nls from 'vs/nls';
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
import { ReplaceInput, IReplaceInputStyles } from 'vs/base/browser/ui/findinput/replaceInput';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { editorWidgetBackground, editorWidgetForeground, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { widgetClose } from 'vs/platform/theme/common/iconRegistry';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
......@@ -34,6 +34,7 @@ const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Repla
const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All");
export abstract class SimpleFindReplaceWidget extends Widget {
protected readonly _findInput: FindInput;
private readonly _domNode: HTMLElement;
......@@ -41,6 +42,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
private readonly _focusTracker: dom.IFocusTracker;
private readonly _findInputFocusTracker: dom.IFocusTracker;
private readonly _updateHistoryDelayer: Delayer<void>;
protected readonly _matchesCount!: HTMLElement;
private readonly prevBtn: SimpleButton;
private readonly nextBtn: SimpleButton;
......@@ -145,6 +147,10 @@ export abstract class SimpleFindReplaceWidget extends Widget {
this.findFirst();
}));
this._matchesCount = document.createElement('div');
this._matchesCount.className = 'matchesCount';
this._updateMatchesCount();
this.prevBtn = this._register(new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL,
icon: findPreviousMatchIcon,
......@@ -170,6 +176,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
}));
this._innerFindDomNode.appendChild(this._findInput.domNode);
this._innerFindDomNode.appendChild(this._matchesCount);
this._innerFindDomNode.appendChild(this.prevBtn.domNode);
this._innerFindDomNode.appendChild(this.nextBtn.domNode);
this._innerFindDomNode.appendChild(closeBtn.domNode);
......@@ -307,6 +314,7 @@ export abstract class SimpleFindReplaceWidget extends Widget {
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
this._updateButtons();
this._updateMatchesCount();
}
private _updateButtons(): void {
......@@ -320,6 +328,8 @@ export abstract class SimpleFindReplaceWidget extends Widget {
this._toggleReplaceBtn.setExpanded(this._isReplaceVisible);
}
protected _updateMatchesCount(): void {
}
override dispose() {
super.dispose();
......
......@@ -4,9 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/notebookFind';
import { alert as alertFn } from 'vs/base/browser/ui/aria/aria';
import * as strings from 'vs/base/common/strings';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, INotebookEditor, CellFindMatch, CellEditState, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED, getNotebookEditorFromEditorPane, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { Range } from 'vs/editor/common/core/range';
import { MATCHES_LIMIT } from 'vs/editor/contrib/find/findModel';
import { FindDecorations } from 'vs/editor/contrib/find/findDecorations';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IModelDeltaDecoration } from 'vs/editor/common/model';
......@@ -28,9 +32,11 @@ import { INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/not
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/findController';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { NLS_MATCHES_LOCATION, NLS_NO_RESULTS } from 'vs/editor/contrib/find/findWidget';
const FIND_HIDE_TRANSITION = 'find-hide-transition';
const FIND_SHOW_TRANSITION = 'find-show-transition';
let MAX_MATCHES_COUNT_WIDTH = 69;
export class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEditorContribution {
......@@ -128,6 +134,13 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote
const nextIndex = this._findMatchesStarts!.getIndexOf(this._currentMatch);
this.setCurrentFindMatchDecoration(nextIndex.index, nextIndex.remainder);
this.revealCellRange(nextIndex.index, nextIndex.remainder);
this._state.changeMatchInfo(
this._currentMatch,
this._findMatches.reduce((p, c) => p + c.matches.length, 0),
undefined
);
// this._updateMatchesCount();
}
protected replaceOne() {
......@@ -221,6 +234,12 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote
this._currentMatch = 0;
this.setCurrentFindMatchDecoration(0, 0);
}
this._state.changeMatchInfo(
this._currentMatch,
this._findMatches.reduce((p, c) => p + c.matches.length, 0),
undefined
);
}
private setCurrentFindMatchDecoration(cellIndex: number, matchIndex: number) {
......@@ -337,6 +356,49 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote
}
}
override _updateMatchesCount(): void {
if (!this._findMatches) {
return;
}
this._matchesCount.style.minWidth = MAX_MATCHES_COUNT_WIDTH + 'px';
this._matchesCount.title = '';
// remove previous content
if (this._matchesCount.firstChild) {
this._matchesCount.removeChild(this._matchesCount.firstChild);
}
let label: string;
if (this._state.matchesCount > 0) {
let matchesCount: string = String(this._state.matchesCount);
if (this._state.matchesCount >= MATCHES_LIMIT) {
matchesCount += '+';
}
let matchesPosition: string = this._currentMatch < 0 ? '?' : String(this._currentMatch);
label = strings.format(NLS_MATCHES_LOCATION, matchesPosition, matchesCount);
} else {
label = NLS_NO_RESULTS;
}
this._matchesCount.appendChild(document.createTextNode(label));
alertFn(this._getAriaLabel(label, this._state.currentMatch, this._state.searchString));
MAX_MATCHES_COUNT_WIDTH = Math.max(MAX_MATCHES_COUNT_WIDTH, this._matchesCount.clientWidth);
}
private _getAriaLabel(label: string, currentMatch: Range | null, searchString: string): string {
if (label === NLS_NO_RESULTS) {
return searchString === ''
? localize('ariaSearchNoResultEmpty', "{0} found", label)
: localize('ariaSearchNoResult', "{0} found for '{1}'", label, searchString);
}
// TODO@rebornix, aria for `cell ${index}, line {line}`
return localize('ariaSearchNoResultWithLineNumNoCurrentMatch', "{0} found for '{1}'", label, searchString);
}
clear() {
this._currentMatch = -1;
this._findMatches = [];
......
......@@ -10,3 +10,7 @@
.monaco-workbench .notebookOverlay.notebook-editor.find-show-transition {
overflow-y: hidden;
}
.monaco-workbench .notebookOverlay.notebook-editor .simple-fr-find-part-wrapper .matchesCount {
text-align: center;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册