提交 1e3b0c69 编写于 作者: B Benjamin Pasero

fix various quick pick issues (fixes #43114)

上级 46c55efc
......@@ -19,8 +19,9 @@ export interface IIconLabelCreationOptions {
supportDescriptionHighlights?: boolean;
}
export interface IIconLabelOptions {
export interface IIconLabelValueOptions {
title?: string;
descriptionTitle?: string;
extraClasses?: string[];
italic?: boolean;
matches?: IMatch[];
......@@ -121,7 +122,7 @@ export class IconLabel {
]);
}
public setValue(label?: string, description?: string, options?: IIconLabelOptions): void {
public setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void {
const classes = ['monaco-icon-label'];
if (options) {
if (options.extraClasses) {
......@@ -149,8 +150,14 @@ export class IconLabel {
if (this.descriptionNode instanceof HighlightedLabel) {
this.descriptionNode.set(description || '', options ? options.descriptionMatches : void 0);
if (options && options.descriptionTitle) {
this.descriptionNode.element.title = options.descriptionTitle;
} else {
this.descriptionNode.element.removeAttribute('title');
}
} else {
this.descriptionNode.textContent = description || '';
this.descriptionNode.title = options && options.descriptionTitle ? options.descriptionTitle : '';
this.descriptionNode.empty = !description;
}
}
......
......@@ -160,8 +160,7 @@ export class InputBox extends Widget {
}
if (this.placeholder) {
this.input.setAttribute('placeholder', this.placeholder);
this.input.title = this.placeholder;
this.setPlaceHolder(this.placeholder);
}
this.oninput(this.input, () => this.onValueChange());
......@@ -204,6 +203,7 @@ export class InputBox extends Widget {
public setPlaceHolder(placeHolder: string): void {
if (this.input) {
this.input.setAttribute('placeholder', placeHolder);
this.input.title = placeHolder;
}
}
......
......@@ -7,7 +7,6 @@
import strings = require('vs/base/common/strings');
import { LRUCache } from 'vs/base/common/map';
import { CharCode } from 'vs/base/common/charCode';
import { ltrim } from 'vs/base/common/strings';
export interface IFilter {
// Returns null if word doesn't match.
......@@ -342,108 +341,6 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep
return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst);
}
const octiconStartMarker = '$(';
export function matchesFuzzyOcticonAware(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] {
// Return early if there are no octicon markers in the word to match against
const firstOcticonIndex = wordToMatchAgainst.indexOf(octiconStartMarker);
if (firstOcticonIndex === -1) {
return matchesFuzzy(word, wordToMatchAgainst, enableSeparateSubstringMatching);
}
const octiconOffsets: number[] = [];
let wordToMatchAgainstWithoutOcticons: string = '';
function appendChars(chars: string) {
if (chars) {
wordToMatchAgainstWithoutOcticons += chars;
for (let i = 0; i < chars.length; i++) {
octiconOffsets.push(octiconsOffset); // make sure to fill in octicon offsets
}
}
}
let currentOcticonStart = -1;
let currentOcticonValue: string = '';
let octiconsOffset = 0;
let char: string;
let nextChar: string;
let offset = firstOcticonIndex;
const length = wordToMatchAgainst.length;
// Append all characters until the first octicon
appendChars(wordToMatchAgainst.substr(0, firstOcticonIndex));
// example: $(file-symlink-file) my cool $(other-octicon) entry
while (offset < length) {
char = wordToMatchAgainst[offset];
nextChar = wordToMatchAgainst[offset + 1];
// beginning of octicon: some value $( <--
if (char === octiconStartMarker[0] && nextChar === octiconStartMarker[1]) {
currentOcticonStart = offset;
// if we had a previous potential octicon value without
// the closing ')', it was actually not an octicon and
// so we have to add it to the actual value
appendChars(currentOcticonValue);
currentOcticonValue = octiconStartMarker;
offset++; // jump over '('
}
// end of octicon: some value $(some-octicon) <--
else if (char === ')' && currentOcticonStart !== -1) {
const currentOcticonLength = offset - currentOcticonStart + 1; // +1 to include the closing ')'
octiconsOffset += currentOcticonLength;
currentOcticonStart = -1;
currentOcticonValue = '';
}
// within octicon
else if (currentOcticonStart !== -1) {
currentOcticonValue += char;
}
// any value outside of octicons
else {
appendChars(char);
}
offset++;
}
// if we had a previous potential octicon value without
// the closing ')', it was actually not an octicon and
// so we have to add it to the actual value
appendChars(currentOcticonValue);
// Trim the word to match against because it could have leading
// whitespace now if the word started with an octicon
const wordToMatchAgainstWithoutOcticonsTrimmed = ltrim(wordToMatchAgainstWithoutOcticons, ' ');
const leadingWhitespaceOffset = wordToMatchAgainstWithoutOcticons.length - wordToMatchAgainstWithoutOcticonsTrimmed.length;
// match on value without octicons
const matches = matchesFuzzy(word, wordToMatchAgainstWithoutOcticonsTrimmed, enableSeparateSubstringMatching);
// Map matches back to offsets with octicons and trimming
if (matches) {
for (let i = 0; i < matches.length; i++) {
const octiconOffset = octiconOffsets[matches[i].start] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
matches[i].start += octiconOffset;
matches[i].end += octiconOffset;
}
}
return matches;
}
export function skipScore(pattern: string, word: string, patternMaxWhitespaceIgnore?: number): [number, number[]] {
pattern = pattern.toLowerCase();
word = word.toLowerCase();
......
/*---------------------------------------------------------------------------------------------
* 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 { matchesFuzzy, IMatch } from 'vs/base/common/filters';
import { ltrim } from 'vs/base/common/strings';
const octiconStartMarker = '$(';
export function removeOcticons(word: string): string {
const firstOcticonIndex = word.indexOf(octiconStartMarker);
if (firstOcticonIndex === -1) {
return word; // return early if the word does not include an octicon
}
return doParseOcticonAware(word, firstOcticonIndex).wordWithoutOcticons.trim();
}
function doParseOcticonAware(word: string, firstOcticonIndex: number): { wordWithoutOcticons: string, octiconOffsets: number[] } {
const octiconOffsets: number[] = [];
let wordWithoutOcticons: string = '';
function appendChars(chars: string) {
if (chars) {
wordWithoutOcticons += chars;
for (let i = 0; i < chars.length; i++) {
octiconOffsets.push(octiconsOffset); // make sure to fill in octicon offsets
}
}
}
let currentOcticonStart = -1;
let currentOcticonValue: string = '';
let octiconsOffset = 0;
let char: string;
let nextChar: string;
let offset = firstOcticonIndex;
const length = word.length;
// Append all characters until the first octicon
appendChars(word.substr(0, firstOcticonIndex));
// example: $(file-symlink-file) my cool $(other-octicon) entry
while (offset < length) {
char = word[offset];
nextChar = word[offset + 1];
// beginning of octicon: some value $( <--
if (char === octiconStartMarker[0] && nextChar === octiconStartMarker[1]) {
currentOcticonStart = offset;
// if we had a previous potential octicon value without
// the closing ')', it was actually not an octicon and
// so we have to add it to the actual value
appendChars(currentOcticonValue);
currentOcticonValue = octiconStartMarker;
offset++; // jump over '('
}
// end of octicon: some value $(some-octicon) <--
else if (char === ')' && currentOcticonStart !== -1) {
const currentOcticonLength = offset - currentOcticonStart + 1; // +1 to include the closing ')'
octiconsOffset += currentOcticonLength;
currentOcticonStart = -1;
currentOcticonValue = '';
}
// within octicon
else if (currentOcticonStart !== -1) {
currentOcticonValue += char;
}
// any value outside of octicons
else {
appendChars(char);
}
offset++;
}
// if we had a previous potential octicon value without
// the closing ')', it was actually not an octicon and
// so we have to add it to the actual value
appendChars(currentOcticonValue);
return { wordWithoutOcticons, octiconOffsets };
}
export function matchesFuzzyOcticonAware(word: string, wordToMatchAgainst: string, enableSeparateSubstringMatching = false): IMatch[] {
// Return early if there are no octicon markers in the word to match against
const firstOcticonIndex = wordToMatchAgainst.indexOf(octiconStartMarker);
if (firstOcticonIndex === -1) {
return matchesFuzzy(word, wordToMatchAgainst, enableSeparateSubstringMatching);
}
// Parse
const { wordWithoutOcticons, octiconOffsets } = doParseOcticonAware(wordToMatchAgainst, firstOcticonIndex);
// Trim the word to match against because it could have leading
// whitespace now if the word started with an octicon
const wordToMatchAgainstWithoutOcticonsTrimmed = ltrim(wordWithoutOcticons, ' ');
const leadingWhitespaceOffset = wordWithoutOcticons.length - wordToMatchAgainstWithoutOcticonsTrimmed.length;
// match on value without octicons
const matches = matchesFuzzy(word, wordToMatchAgainstWithoutOcticonsTrimmed, enableSeparateSubstringMatching);
// Map matches back to offsets with octicons and trimming
if (matches) {
for (let i = 0; i < matches.length; i++) {
const octiconOffset = octiconOffsets[matches[i].start] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */;
matches[i].start += octiconOffset;
matches[i].end += octiconOffset;
}
}
return matches;
}
\ No newline at end of file
......@@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import types = require('vs/base/common/types');
import URI from 'vs/base/common/uri';
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { IconLabel, IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode } from 'vs/base/parts/quickopen/common/quickOpen';
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
import { compareAnything } from 'vs/base/common/comparers';
......@@ -84,7 +84,7 @@ export class QuickOpenEntry {
/**
* The options for the label to use for this entry
*/
public getLabelOptions(): IIconLabelOptions {
public getLabelOptions(): IIconLabelValueOptions {
return null;
}
......@@ -123,6 +123,13 @@ export class QuickOpenEntry {
return null;
}
/**
* A tooltip to show when hovering over the description portion of the entry.
*/
public getDescriptionTooltip(): string {
return null;
}
/**
* An optional keybinding to show for an entry.
*/
......@@ -227,7 +234,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry {
return this.entry ? this.entry.getLabel() : super.getLabel();
}
public getLabelOptions(): IIconLabelOptions {
public getLabelOptions(): IIconLabelValueOptions {
return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions();
}
......@@ -459,9 +466,10 @@ class Renderer implements IRenderer<QuickOpenEntry> {
data.icon.className = iconClass;
// Label
const options: IIconLabelOptions = entry.getLabelOptions() || Object.create(null);
const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null);
options.matches = labelHighlights || [];
options.title = entry.getTooltip() || void 0;
options.title = entry.getTooltip();
options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow
options.descriptionMatches = descriptionHighlights || [];
data.label.setValue(entry.getLabel(), entry.getDescription(), options);
......
......@@ -5,7 +5,8 @@
'use strict';
import * as assert from 'assert';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, matchesFuzzy, matchesFuzzyOcticonAware } from 'vs/base/common/filters';
import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, matchesFuzzy } from 'vs/base/common/filters';
import { matchesFuzzyOcticonAware } from 'vs/base/common/octicon';
function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) {
let r = filter(word, wordToMatchAgainst);
......
......@@ -7,7 +7,7 @@
import uri from 'vs/base/common/uri';
import resources = require('vs/base/common/resources');
import { IconLabel, IIconLabelOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IEditorInput } from 'vs/platform/editor/common/editor';
......@@ -32,7 +32,7 @@ export interface IResourceLabel {
resource?: uri;
}
export interface IResourceLabelOptions extends IIconLabelOptions {
export interface IResourceLabelOptions extends IIconLabelValueOptions {
fileKind?: FileKind;
fileDecorations?: { colors: boolean, badges: boolean, data?: IDecorationData };
}
......@@ -172,7 +172,7 @@ export class ResourceLabel extends IconLabel {
return;
}
const iconLabelOptions: IIconLabelOptions = {
const iconLabelOptions: IIconLabelValueOptions = {
title: '',
italic: this.options && this.options.italic,
matches: this.options && this.options.matches,
......
......@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -39,7 +39,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
this.stacks = editorGroupService.getStacksModel();
}
public getLabelOptions(): IIconLabelOptions {
public getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
italic: this._group.isPreview(this.editor)
......
......@@ -17,7 +17,7 @@ import * as resources from 'vs/base/common/resources';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import types = require('vs/base/common/types');
import { Action, IAction } from 'vs/base/common/actions';
import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, QuickOpenItemAccessorClass } from 'vs/base/parts/quickopen/browser/quickOpenModel';
......@@ -54,7 +54,7 @@ import { FileKind, IFileService } from 'vs/platform/files/common/files';
import { scoreItem, ScorerCache, compareItemsByScore, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { getBaseLabel } from 'vs/base/common/labels';
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
import { matchesFuzzyOcticonAware } from 'vs/base/common/filters';
import { matchesFuzzyOcticonAware, removeOcticons } from 'vs/base/common/octicon';
const HELP_PREFIX = '?';
......@@ -1025,6 +1025,7 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
private description: string;
private detail: string;
private tooltip: string;
private descriptionTooltip: string;
private hasSeparator: boolean;
private separatorLabel: string;
private alwaysShow: boolean;
......@@ -1047,6 +1048,7 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
this.description = item.description;
this.detail = item.detail;
this.tooltip = item.tooltip;
this.descriptionTooltip = item.description ? removeOcticons(item.description) : void 0;
this.hasSeparator = item.separator && item.separator.border;
this.separatorLabel = item.separator && item.separator.label;
this.alwaysShow = item.alwaysShow;
......@@ -1081,7 +1083,7 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
return this._index;
}
public getLabelOptions(): IIconLabelOptions {
public getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: this.resource ? getIconClasses(this.modelService, this.modeService, this.resource, this.fileKind) : []
};
......@@ -1103,6 +1105,10 @@ class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem {
return this.tooltip;
}
public getDescriptionTooltip(): string {
return this.descriptionTooltip;
}
public showBorder(): boolean {
return this.hasSeparator;
}
......@@ -1282,7 +1288,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
return this.label;
}
public getLabelOptions(): IIconLabelOptions {
public getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.resource)
};
......
......@@ -13,7 +13,7 @@ import * as objects from 'vs/base/common/objects';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import URI from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IIconLabelOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { getIconClasses } from 'vs/workbench/browser/labels';
import { IModelService } from 'vs/editor/common/services/modelService';
......@@ -62,7 +62,7 @@ export class FileEntry extends EditorQuickOpenEntry {
return this.name;
}
public getLabelOptions(): IIconLabelOptions {
public getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.resource)
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册