提交 fd6bd5e9 编写于 作者: S Sandeep Somavarapu

Clean up: Separate actions, widgets from SearchViewlet

上级 39d931c7
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import nls = require('vs/nls');
import strings = require('vs/base/common/strings');
import { $ } from 'vs/base/browser/builder';
import {IExpression, splitGlobAware} from 'vs/base/common/glob';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { MessageType, InputBox, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox';
export interface IOptions {
placeholder?: string;
width?: number;
validation?: IInputValidator;
ariaLabel?: string;
}
export class PatternInput {
static OPTION_CHANGE: string = 'optionChange';
private contextViewProvider: IContextViewProvider;
private onOptionChange: (event: Event) => void;
private width: number;
private placeholder: string;
private ariaLabel: string;
private listenersToRemove: any[];
private pattern: Checkbox;
public domNode: HTMLElement;
private inputNode: HTMLInputElement;
private inputBox: InputBox;
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null)) {
this.contextViewProvider = contextViewProvider;
this.onOptionChange = null;
this.width = options.width || 100;
this.placeholder = options.placeholder || '';
this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input");
this.listenersToRemove = [];
this.pattern = null;
this.domNode = null;
this.inputNode = null;
this.inputBox = null;
this.buildDomNode();
if (Boolean(parent)) {
parent.appendChild(this.domNode);
}
}
public destroy(): void {
this.pattern.dispose();
this.listenersToRemove.forEach((element) => {
element();
});
this.listenersToRemove = [];
}
public on(eventType: string, handler: (event: Event) => void): PatternInput {
switch (eventType) {
case 'keydown':
case 'keyup':
$(this.inputBox.inputElement).on(eventType, handler);
break;
case PatternInput.OPTION_CHANGE:
this.onOptionChange = handler;
break;
}
return this;
}
public setWidth(newWidth: number): void {
this.width = newWidth;
this.domNode.style.width = this.width + 'px';
this.contextViewProvider.layout();
this.setInputWidth();
}
public getValue(): string {
return this.inputBox.value;
}
public setValue(value: string): void {
if (this.inputBox.value !== value) {
this.inputBox.value = value;
}
}
public getGlob(): IExpression {
let pattern = this.getValue();
let isGlobPattern = this.isGlobPattern();
if (!pattern) {
return void 0;
}
let glob: IExpression = Object.create(null);
let segments: string[];
if (isGlobPattern) {
segments = splitGlobAware(pattern, ',').map(s => s.trim()).filter(s => !!s.length);
} else {
segments = pattern.split(',').map(s => strings.trim(s.trim(), '/')).filter(s => !!s.length).map(p => {
if (p[0] === '.') {
p = '*' + p; // convert ".js" to "*.js"
}
return strings.format('{{0}/**,**/{1}}', p, p); // convert foo to {foo/**,**/foo} to cover files and folders
});
}
return segments.reduce((prev, cur) => { glob[cur] = true; return glob; }, glob);
}
public select(): void {
this.inputBox.select();
}
public focus(): void {
this.inputBox.focus();
}
public isGlobPattern(): boolean {
return this.pattern.checked;
}
public setIsGlobPattern(value: boolean): void {
this.pattern.checked = value;
this.setInputWidth();
}
private setInputWidth(): void {
let w = this.width - this.pattern.width();
this.inputBox.width = w;
}
private buildDomNode(): void {
this.domNode = document.createElement('div');
this.domNode.style.width = this.width + 'px';
$(this.domNode).addClass('monaco-findInput');
this.inputBox = new InputBox(this.domNode, this.contextViewProvider, {
placeholder: this.placeholder || '',
ariaLabel: this.ariaLabel || '',
validationOptions: {
validation: null,
showMessage: true
}
});
this.pattern = new Checkbox({
actionClassName: 'pattern',
title: nls.localize('patternDescription', "Use Glob Patterns"),
isChecked: false,
onChange: (viaKeyboard) => {
this.onOptionChange(null);
if (!viaKeyboard) {
this.inputBox.focus();
}
this.setInputWidth();
if (this.isGlobPattern()) {
this.showGlobHelp();
} else {
this.inputBox.hideMessage();
}
}
});
$(this.pattern.domNode).on('mouseover', () => {
if (this.isGlobPattern()) {
this.showGlobHelp();
}
});
$(this.pattern.domNode).on(['mouseleave', 'mouseout'], () => {
this.inputBox.hideMessage();
});
this.setInputWidth();
let controls = document.createElement('div');
controls.className = 'controls';
controls.appendChild(this.pattern.domNode);
this.domNode.appendChild(controls);
}
private showGlobHelp(): void {
this.inputBox.showMessage({
type: MessageType.INFO,
formatContent: true,
content: nls.localize('patternHelpInclude',
"The pattern to match. e.g. **\\*\\*/*.js** to match all JavaScript files or **myFolder/\\*\\*** to match that folder with all children.\n\n**Reference**:\n**\\*** matches 0 or more characters\n**?** matches 1 character\n**\\*\\*** matches zero or more directories\n**[a-z]** matches a range of characters\n**{a,b}** matches any of the patterns)"
)
}, true);
}
}
......@@ -26,8 +26,7 @@ import {IQuickOpenService} from 'vs/workbench/services/quickopen/common/quickOpe
import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {OpenSearchViewletAction} from 'vs/workbench/parts/search/browser/searchActions';
export const VIEWLET_ID = 'workbench.view.search';
import {VIEWLET_ID} from 'vs/workbench/parts/search/common/constants';
KeybindingsRegistry.registerCommandDesc({
id: 'workbench.action.search.toggleQueryDetails',
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import nls = require('vs/nls');
import strings = require('vs/base/common/strings');
import platform = require('vs/base/common/platform');
import errors = require('vs/base/common/errors');
import paths = require('vs/base/common/paths');
import dom = require('vs/base/browser/dom');
import { $ } from 'vs/base/browser/builder';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { ActionsRenderer } from 'vs/base/parts/tree/browser/actionsRenderer';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { FileLabel } from 'vs/base/browser/ui/fileLabel/fileLabel';
import { LeftRightWidget, IRenderer } from 'vs/base/browser/ui/leftRightWidget/leftRightWidget';
import { ITree, IElementCallback, IDataSource, ISorter, IAccessibilityProvider, IFilter } from 'vs/base/parts/tree/browser/tree';
import {ClickBehavior, DefaultController} from 'vs/base/parts/tree/browser/treeDefaults';
import { ContributableActionProvider } from 'vs/workbench/browser/actionBarRegistry';
import { Match, EmptyMatch, SearchResult, FileMatch} from 'vs/workbench/parts/search/common/searchModel';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Range } from 'vs/editor/common/core/range';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { CommonKeybindings} from 'vs/base/common/keyCodes';
import { SearchViewlet } from 'vs/workbench/parts/search/browser/searchViewlet';
import { RemoveAction } from 'vs/workbench/parts/search/browser/searchActions';
export type FileMatchOrMatch = FileMatch | Match;
export class SearchDataSource implements IDataSource {
public getId(tree: ITree, element: any): string {
if (element instanceof FileMatch) {
return element.id();
}
if (element instanceof Match) {
return element.id();
}
return 'root';
}
public getChildren(tree: ITree, element: any): TPromise<any[]> {
let value: any[] = [];
if (element instanceof FileMatch) {
value = element.matches();
} else if (element instanceof SearchResult) {
value = element.matches();
}
return TPromise.as(value);
}
public hasChildren(tree: ITree, element: any): boolean {
return element instanceof FileMatch || element instanceof SearchResult;
}
public getParent(tree: ITree, element: any): TPromise<any> {
let value: any = null;
if (element instanceof Match) {
value = element.parent();
} else if (element instanceof FileMatch) {
value = element.parent();
}
return TPromise.as(value);
}
}
export class SearchSorter implements ISorter {
public compare(tree: ITree, elementA: FileMatchOrMatch, elementB: FileMatchOrMatch): number {
if (elementA instanceof FileMatch && elementB instanceof FileMatch) {
return strings.localeCompare(elementA.resource().fsPath, elementB.resource().fsPath) || strings.localeCompare(elementA.name(), elementB.name());
}
if (elementA instanceof Match && elementB instanceof Match) {
return Range.compareRangesUsingStarts(elementA.range(), elementB.range());
}
}
}
class SearchActionProvider extends ContributableActionProvider {
public hasActions(tree: ITree, element: any): boolean {
return element instanceof FileMatch || super.hasActions(tree, element);
}
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
return super.getActions(tree, element).then(actions => {
if (element instanceof FileMatch) {
actions.unshift(new RemoveAction(tree, element));
}
return actions;
});
}
}
export class SearchRenderer extends ActionsRenderer {
constructor(actionRunner: IActionRunner, @IWorkspaceContextService private contextService: IWorkspaceContextService) {
super({
actionProvider: new SearchActionProvider(),
actionRunner: actionRunner
});
}
public getContentHeight(tree: ITree, element: any): number {
return 22;
}
public renderContents(tree: ITree, element: FileMatchOrMatch, domElement: HTMLElement, previousCleanupFn: IElementCallback): IElementCallback {
// File
if (element instanceof FileMatch) {
let fileMatch = <FileMatch>element;
let container = $('.filematch');
let leftRenderer: IRenderer;
let rightRenderer: IRenderer;
let widget: LeftRightWidget;
leftRenderer = (left: HTMLElement): any => {
new FileLabel(left, fileMatch.resource(), this.contextService);
return null;
};
rightRenderer = (right: HTMLElement) => {
let len = fileMatch.count();
return new CountBadge(right, len, len > 1 ? nls.localize('searchMatches', "{0} matches found", len) : nls.localize('searchMatch', "{0} match found", len));
};
widget = new LeftRightWidget(container, leftRenderer, rightRenderer);
container.appendTo(domElement);
return widget.dispose.bind(widget);
}
// Empty
else if (element instanceof EmptyMatch) {
dom.addClass(domElement, 'linematch');
$('a.plain.label').innerHtml(nls.localize('noMatches', "no matches")).appendTo(domElement);
}
// Match
else if (element instanceof Match) {
dom.addClass(domElement, 'linematch');
let elements: string[] = [];
let preview = element.preview();
elements.push('<span>');
elements.push(strings.escape(preview.before));
elements.push('</span><span class="findInFileMatch">');
elements.push(strings.escape(preview.inside));
elements.push('</span><span>');
elements.push(strings.escape(preview.after));
elements.push('</span>');
$('a.plain')
.innerHtml(elements.join(strings.empty))
.title((preview.before + preview.inside + preview.after).trim().substr(0, 999))
.appendTo(domElement);
}
return null;
}
}
export class SearchAccessibilityProvider implements IAccessibilityProvider {
constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
}
public getAriaLabel(tree: ITree, element: FileMatchOrMatch): string {
if (element instanceof FileMatch) {
const path = this.contextService.toWorkspaceRelativePath(element.resource()) || element.resource().fsPath;
return nls.localize('fileMatchAriaLabel', "{0} matches in file {1} of folder {2}, Search result", element.count(), element.name(), paths.dirname(path));
}
if (element instanceof Match) {
return nls.localize('searchResultAria', "{0}, Search result", element.text());
}
}
}
export class SearchController extends DefaultController {
constructor(private viewlet: SearchViewlet) {
super({ clickBehavior: ClickBehavior.ON_MOUSE_DOWN });
if (platform.isMacintosh) {
this.downKeyBindingDispatcher.set(CommonKeybindings.CTRLCMD_BACKSPACE, (tree: ITree, event: any) => { this.onDelete(tree, event); });
this.upKeyBindingDispatcher.set(CommonKeybindings.WINCTRL_ENTER, this.onEnter.bind(this));
} else {
this.downKeyBindingDispatcher.set(CommonKeybindings.DELETE, (tree: ITree, event: any) => { this.onDelete(tree, event); });
this.upKeyBindingDispatcher.set(CommonKeybindings.CTRLCMD_ENTER, this.onEnter.bind(this));
}
this.downKeyBindingDispatcher.set(CommonKeybindings.ESCAPE, (tree: ITree, event: any) => { this.onEscape(tree, event); });
}
protected onEscape(tree: ITree, event: IKeyboardEvent): boolean {
if (this.viewlet.cancelSearch()) {
return true;
}
return super.onEscape(tree, event);
}
private onDelete(tree: ITree, event: IKeyboardEvent): boolean {
let result = false;
let element = tree.getFocus();
if (element instanceof FileMatch) {
new RemoveAction(tree, element).run().done(null, errors.onUnexpectedError);
result = true;
}
return result;
}
}
export class SearchFilter implements IFilter {
public isVisible(tree: ITree, element: any): boolean {
return !(element instanceof FileMatch) || element.matches().length > 0;
}
}
\ No newline at end of file
......@@ -6,32 +6,24 @@
'use strict';
import 'vs/css!./media/searchviewlet';
import {TPromise, PPromise} from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import platform = require('vs/base/common/platform');
import {TPromise, PPromise} from 'vs/base/common/winjs.base';
import {EditorType} from 'vs/editor/common/editorCommon';
import lifecycle = require('vs/base/common/lifecycle');
import errors = require('vs/base/common/errors');
import aria = require('vs/base/browser/ui/aria/aria');
import {IExpression, splitGlobAware} from 'vs/base/common/glob';
import { IExpression } from 'vs/base/common/glob';
import {isFunction} from 'vs/base/common/types';
import URI from 'vs/base/common/uri';
import strings = require('vs/base/common/strings');
import paths = require('vs/base/common/paths');
import dom = require('vs/base/browser/dom');
import {IAction, Action, IActionRunner} from 'vs/base/common/actions';
import {IAction, Action} from 'vs/base/common/actions';
import {StandardKeyboardEvent, IKeyboardEvent} from 'vs/base/browser/keyboardEvent';
import timer = require('vs/base/common/timer');
import {Dimension, Builder, $} from 'vs/base/browser/builder';
import {FileLabel} from 'vs/base/browser/ui/fileLabel/fileLabel';
import {FindInput, IFindInputOptions} from 'vs/base/browser/ui/findinput/findInput';
import {LeftRightWidget, IRenderer} from 'vs/base/browser/ui/leftRightWidget/leftRightWidget';
import {CountBadge} from 'vs/base/browser/ui/countBadge/countBadge';
import {ITree, IElementCallback, IFilter, ISorter, IDataSource, IAccessibilityProvider} from 'vs/base/parts/tree/browser/tree';
import {ITree} from 'vs/base/parts/tree/browser/tree';
import {Tree} from 'vs/base/parts/tree/browser/treeImpl';
import {ClickBehavior, DefaultController} from 'vs/base/parts/tree/browser/treeDefaults';
import {ActionsRenderer} from 'vs/base/parts/tree/browser/actionsRenderer';
import {ContributableActionProvider} from 'vs/workbench/browser/actionBarRegistry';
import {Scope} from 'vs/workbench/common/memento';
import {OpenGlobalSettingsAction} from 'vs/workbench/browser/actions/openSettings';
import {UntitledEditorEvent, EventType as WorkbenchEventType} from 'vs/workbench/common/events';
......@@ -39,15 +31,12 @@ import {IEditorGroupService} from 'vs/workbench/services/group/common/groupServi
import {getOutOfWorkspaceEditorResources} from 'vs/workbench/common/editor';
import {FileChangeType, FileChangesEvent, EventType as FileEventType} from 'vs/platform/files/common/files';
import {Viewlet} from 'vs/workbench/browser/viewlet';
import {Match, EmptyMatch, SearchResult, FileMatch} from 'vs/workbench/parts/search/common/searchModel';
import {Match, EmptyMatch, SearchResult} from 'vs/workbench/parts/search/common/searchModel';
import {getExcludes, QueryBuilder} from 'vs/workbench/parts/search/common/searchQuery';
import {Checkbox} from 'vs/base/browser/ui/checkbox/checkbox';
import {VIEWLET_ID} from 'vs/workbench/parts/search/browser/search.contribution';
import {MessageType, InputBox, IInputValidator} from 'vs/base/browser/ui/inputbox/inputBox';
import {IContextViewProvider} from 'vs/base/browser/ui/contextview/contextview';
import {VIEWLET_ID} from 'vs/workbench/parts/search/common/constants';
import {MessageType, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import {ISearchProgressItem, IFileMatch, ISearchComplete, ISearchQuery, IQueryOptions, ISearchConfiguration} from 'vs/platform/search/common/search';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {Range} from 'vs/editor/common/core/range';
import {IStorageService} from 'vs/platform/storage/common/storage';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
......@@ -60,410 +49,9 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
import {KeyCode, CommonKeybindings} from 'vs/base/common/keyCodes';
import { RemoveAction, RefreshAction, SelectOrRemoveAction, CollapseAllAction, ClearSearchResultsAction, ConfigureGlobalExclusionsAction } from 'vs/workbench/parts/search/browser/searchActions';
const ID = VIEWLET_ID;
export class SearchDataSource implements IDataSource {
public getId(tree: ITree, element: any): string {
if (element instanceof FileMatch) {
return element.id();
}
if (element instanceof Match) {
return element.id();
}
return 'root';
}
public getChildren(tree: ITree, element: any): TPromise<any[]> {
let value: any[] = [];
if (element instanceof FileMatch) {
value = element.matches();
} else if (element instanceof SearchResult) {
value = element.matches();
}
return TPromise.as(value);
}
public hasChildren(tree: ITree, element: any): boolean {
return element instanceof FileMatch || element instanceof SearchResult;
}
public getParent(tree: ITree, element: any): TPromise<any> {
let value: any = null;
if (element instanceof Match) {
value = element.parent();
} else if (element instanceof FileMatch) {
value = element.parent();
}
return TPromise.as(value);
}
}
export type FileMatchOrMatch = FileMatch | Match;
export class SearchSorter implements ISorter {
public compare(tree: ITree, elementA: FileMatchOrMatch, elementB: FileMatchOrMatch): number {
if (elementA instanceof FileMatch && elementB instanceof FileMatch) {
return strings.localeCompare(elementA.resource().fsPath, elementB.resource().fsPath) || strings.localeCompare(elementA.name(), elementB.name());
}
if (elementA instanceof Match && elementB instanceof Match) {
return Range.compareRangesUsingStarts(elementA.range(), elementB.range());
}
}
}
export class SearchAccessibilityProvider implements IAccessibilityProvider {
constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService) {
}
public getAriaLabel(tree: ITree, element: FileMatchOrMatch): string {
if (element instanceof FileMatch) {
const path = this.contextService.toWorkspaceRelativePath(element.resource()) || element.resource().fsPath;
return nls.localize('fileMatchAriaLabel', "{0} matches in file {1} of folder {2}, Search result", element.count(), element.name(), paths.dirname(path));
}
if (element instanceof Match) {
return nls.localize('searchResultAria', "{0}, Search result", element.text());
}
}
}
class SearchController extends DefaultController {
constructor(private viewlet: SearchViewlet) {
super({ clickBehavior: ClickBehavior.ON_MOUSE_DOWN });
if (platform.isMacintosh) {
this.downKeyBindingDispatcher.set(CommonKeybindings.CTRLCMD_BACKSPACE, (tree: ITree, event: any) => { this.onDelete(tree, event); });
this.upKeyBindingDispatcher.set(CommonKeybindings.WINCTRL_ENTER, this.onEnter.bind(this));
} else {
this.downKeyBindingDispatcher.set(CommonKeybindings.DELETE, (tree: ITree, event: any) => { this.onDelete(tree, event); });
this.upKeyBindingDispatcher.set(CommonKeybindings.CTRLCMD_ENTER, this.onEnter.bind(this));
}
this.downKeyBindingDispatcher.set(CommonKeybindings.ESCAPE, (tree: ITree, event: any) => { this.onEscape(tree, event); });
}
protected onEscape(tree: ITree, event: IKeyboardEvent): boolean {
if (this.viewlet.cancelSearch()) {
return true;
}
return super.onEscape(tree, event);
}
private onDelete(tree: ITree, event: IKeyboardEvent): boolean {
let result = false;
let element = tree.getFocus();
if (element instanceof FileMatch) {
new RemoveAction(tree, element).run().done(null, errors.onUnexpectedError);
result = true;
}
return result;
}
}
class SearchFilter implements IFilter {
public isVisible(tree: ITree, element: any): boolean {
return !(element instanceof FileMatch) || element.matches().length > 0;
}
}
class SearchActionProvider extends ContributableActionProvider {
public hasActions(tree: ITree, element: any): boolean {
return element instanceof FileMatch || super.hasActions(tree, element);
}
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
return super.getActions(tree, element).then(actions => {
if (element instanceof FileMatch) {
actions.unshift(new RemoveAction(tree, element));
}
return actions;
});
}
}
class SearchRenderer extends ActionsRenderer {
constructor(actionRunner: IActionRunner, @IWorkspaceContextService private contextService: IWorkspaceContextService) {
super({
actionProvider: new SearchActionProvider(),
actionRunner: actionRunner
});
}
public getContentHeight(tree: ITree, element: any): number {
return 22;
}
public renderContents(tree: ITree, element: FileMatchOrMatch, domElement: HTMLElement, previousCleanupFn: IElementCallback): IElementCallback {
// File
if (element instanceof FileMatch) {
let fileMatch = <FileMatch>element;
let container = $('.filematch');
let leftRenderer: IRenderer;
let rightRenderer: IRenderer;
let widget: LeftRightWidget;
leftRenderer = (left: HTMLElement): any => {
new FileLabel(left, fileMatch.resource(), this.contextService);
return null;
};
rightRenderer = (right: HTMLElement) => {
let len = fileMatch.count();
return new CountBadge(right, len, len > 1 ? nls.localize('searchMatches', "{0} matches found", len) : nls.localize('searchMatch', "{0} match found", len));
};
widget = new LeftRightWidget(container, leftRenderer, rightRenderer);
container.appendTo(domElement);
return widget.dispose.bind(widget);
}
// Empty
else if (element instanceof EmptyMatch) {
dom.addClass(domElement, 'linematch');
$('a.plain.label').innerHtml(nls.localize('noMatches', "no matches")).appendTo(domElement);
}
// Match
else if (element instanceof Match) {
dom.addClass(domElement, 'linematch');
let elements: string[] = [];
let preview = element.preview();
elements.push('<span>');
elements.push(strings.escape(preview.before));
elements.push('</span><span class="findInFileMatch">');
elements.push(strings.escape(preview.inside));
elements.push('</span><span>');
elements.push(strings.escape(preview.after));
elements.push('</span>');
$('a.plain')
.innerHtml(elements.join(strings.empty))
.title((preview.before + preview.inside + preview.after).trim().substr(0, 999))
.appendTo(domElement);
}
return null;
}
}
interface IOptions {
placeholder?: string;
width?: number;
validation?: IInputValidator;
ariaLabel?: string;
}
class PatternInput {
static OPTION_CHANGE: string = 'optionChange';
private contextViewProvider: IContextViewProvider;
private onOptionChange: (event: Event) => void;
private width: number;
private placeholder: string;
private ariaLabel: string;
private listenersToRemove: any[];
private pattern: Checkbox;
public domNode: HTMLElement;
private inputNode: HTMLInputElement;
private inputBox: InputBox;
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null)) {
this.contextViewProvider = contextViewProvider;
this.onOptionChange = null;
this.width = options.width || 100;
this.placeholder = options.placeholder || '';
this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input");
this.listenersToRemove = [];
this.pattern = null;
this.domNode = null;
this.inputNode = null;
this.inputBox = null;
this.buildDomNode();
if (Boolean(parent)) {
parent.appendChild(this.domNode);
}
}
public destroy(): void {
this.pattern.dispose();
this.listenersToRemove.forEach((element) => {
element();
});
this.listenersToRemove = [];
}
public on(eventType: string, handler: (event: Event) => void): PatternInput {
switch (eventType) {
case 'keydown':
case 'keyup':
$(this.inputBox.inputElement).on(eventType, handler);
break;
case PatternInput.OPTION_CHANGE:
this.onOptionChange = handler;
break;
}
return this;
}
public setWidth(newWidth: number): void {
this.width = newWidth;
this.domNode.style.width = this.width + 'px';
this.contextViewProvider.layout();
this.setInputWidth();
}
public getValue(): string {
return this.inputBox.value;
}
public setValue(value: string): void {
if (this.inputBox.value !== value) {
this.inputBox.value = value;
}
}
public getGlob(): IExpression {
let pattern = this.getValue();
let isGlobPattern = this.isGlobPattern();
if (!pattern) {
return void 0;
}
let glob: IExpression = Object.create(null);
let segments: string[];
if (isGlobPattern) {
segments = splitGlobAware(pattern, ',').map(s => s.trim()).filter(s => !!s.length);
} else {
segments = pattern.split(',').map(s => strings.trim(s.trim(), '/')).filter(s => !!s.length).map(p => {
if (p[0] === '.') {
p = '*' + p; // convert ".js" to "*.js"
}
return strings.format('{{0}/**,**/{1}}', p, p); // convert foo to {foo/**,**/foo} to cover files and folders
});
}
return segments.reduce((prev, cur) => { glob[cur] = true; return glob; }, glob);
}
public select(): void {
this.inputBox.select();
}
public focus(): void {
this.inputBox.focus();
}
public isGlobPattern(): boolean {
return this.pattern.checked;
}
public setIsGlobPattern(value: boolean): void {
this.pattern.checked = value;
this.setInputWidth();
}
private setInputWidth(): void {
let w = this.width - this.pattern.width();
this.inputBox.width = w;
}
private buildDomNode(): void {
this.domNode = document.createElement('div');
this.domNode.style.width = this.width + 'px';
$(this.domNode).addClass('monaco-findInput');
this.inputBox = new InputBox(this.domNode, this.contextViewProvider, {
placeholder: this.placeholder || '',
ariaLabel: this.ariaLabel || '',
validationOptions: {
validation: null,
showMessage: true
}
});
this.pattern = new Checkbox({
actionClassName: 'pattern',
title: nls.localize('patternDescription', "Use Glob Patterns"),
isChecked: false,
onChange: (viaKeyboard) => {
this.onOptionChange(null);
if (!viaKeyboard) {
this.inputBox.focus();
}
this.setInputWidth();
if (this.isGlobPattern()) {
this.showGlobHelp();
} else {
this.inputBox.hideMessage();
}
}
});
$(this.pattern.domNode).on('mouseover', () => {
if (this.isGlobPattern()) {
this.showGlobHelp();
}
});
$(this.pattern.domNode).on(['mouseleave', 'mouseout'], () => {
this.inputBox.hideMessage();
});
this.setInputWidth();
let controls = document.createElement('div');
controls.className = 'controls';
controls.appendChild(this.pattern.domNode);
this.domNode.appendChild(controls);
}
private showGlobHelp(): void {
this.inputBox.showMessage({
type: MessageType.INFO,
formatContent: true,
content: nls.localize('patternHelpInclude',
"The pattern to match. e.g. **\\*\\*/*.js** to match all JavaScript files or **myFolder/\\*\\*** to match that folder with all children.\n\n**Reference**:\n**\\*** matches 0 or more characters\n**?** matches 1 character\n**\\*\\*** matches zero or more directories\n**[a-z]** matches a range of characters\n**{a,b}** matches any of the patterns)"
)
}, true);
}
}
import { PatternInput } from 'vs/workbench/parts/search/browser/patternInputWidget';
import { SearchRenderer, SearchDataSource, SearchSorter, SearchController, SearchAccessibilityProvider, SearchFilter } from 'vs/workbench/parts/search/browser/searchResultsView';
import { RefreshAction, SelectOrRemoveAction, CollapseAllAction, ClearSearchResultsAction, ConfigureGlobalExclusionsAction } from 'vs/workbench/parts/search/browser/searchActions';
export class SearchViewlet extends Viewlet {
......@@ -507,7 +95,7 @@ export class SearchViewlet extends Viewlet {
@ISearchService private searchService: ISearchService,
@IKeybindingService keybindingService: IKeybindingService
) {
super(ID, telemetryService);
super(VIEWLET_ID, telemetryService);
this.viewletVisible = keybindingService.createKey<boolean>('searchViewletVisible', true);
this.callOnModelChange = [];
......
......@@ -13,7 +13,7 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {IModelService} from 'vs/editor/common/services/modelService';
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
import {SearchSorter, SearchDataSource} from 'vs/workbench/parts/search/browser/searchViewlet';
import {SearchSorter, SearchDataSource} from 'vs/workbench/parts/search/browser/searchResultsView';
import {TestContextService} from 'vs/workbench/test/common/servicesTestUtils';
suite('Search - Viewlet', () => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册