diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 868c8b5d0e6eab6e1307854a5cd2f685b55b2213..81c35ff625c95f2fb9615b42c1048eac27f65111 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -7,6 +7,7 @@ import 'vs/css!./suggest'; import * as nls from 'vs/nls'; +import * as strings from 'vs/base/common/strings'; import {isPromiseCanceledError, onUnexpectedError} from 'vs/base/common/errors'; import Event, { Emitter } from 'vs/base/common/event'; import {IDisposable, disposeAll} from 'vs/base/common/lifecycle'; @@ -76,7 +77,12 @@ class Renderer implements IRenderer { const data = templateData; const suggestion = (element).suggestion; - data.root.setAttribute('aria-label', nls.localize('suggestionAriaLabel', "{0}, suggestion", suggestion.label)); + if (suggestion.documentationLabel) { + data.root.setAttribute('aria-label', nls.localize('suggestionWithDetailsAriaLabel', "{0}, suggestion, has details", suggestion.label)); + } else { + data.root.setAttribute('aria-label', nls.localize('suggestionAriaLabel', "{0}, suggestion", suggestion.label)); + } + if (suggestion.type && suggestion.type.charAt(0) === '#') { data.icon.className = 'icon customcolor'; data.colorspan.style.backgroundColor = suggestion.type.substring(1); @@ -178,6 +184,7 @@ class SuggestionDetails { private body: HTMLElement; private type: HTMLElement; private docs: HTMLElement; + private ariaLabel:string; constructor(container: HTMLElement, private widget: SuggestWidget) { this.el = append(container, $('.details')); @@ -190,6 +197,8 @@ class SuggestionDetails { append(this.el, this.scrollable.getDomNode()); this.type = append(this.body, $('p.type')); this.docs = append(this.body, $('p.docs')); + + this.ariaLabel = null; } get element() { @@ -201,6 +210,7 @@ class SuggestionDetails { this.title.textContent = ''; this.type.textContent = ''; this.docs.textContent = ''; + this.ariaLabel = null; return; } @@ -219,6 +229,12 @@ class SuggestionDetails { this.scrollable.onElementDimensions(); this.scrollable.onElementInternalDimensions(); + + this.ariaLabel = strings.format('{0}\n{1}\n{2}', item.suggestion.label, item.suggestion.typeLabel, item.suggestion.documentationLabel); + } + + getAriaLabel(): string { + return this.ariaLabel; } scrollDown(much = 8): void { @@ -384,7 +400,25 @@ export class SuggestWidget implements IContentWidget, IDisposable { }, 0); } - private _lastSuggestionLabel: string; + private _getSuggestionAriaAlertLabel(item:CompletionItem): string { + if (item.suggestion.documentationLabel) { + return nls.localize('ariaCurrentSuggestionWithDetails',"{0}, suggestion, has details", item.suggestion.label); + } else { + return nls.localize('ariaCurrentSuggestion',"{0}, suggestion", item.suggestion.label); + } + } + + private _lastAriaAlertLabel: string; + private _ariaAlert(newAriaAlertLabel:string): void { + if (this._lastAriaAlertLabel === newAriaAlertLabel) { + return; + } + this._lastAriaAlertLabel = newAriaAlertLabel; + if (this._lastAriaAlertLabel) { + alert(this._lastAriaAlertLabel); + } + } + private onListFocus(e: IFocusChangeEvent): void { if (this.currentSuggestionDetails) { this.currentSuggestionDetails.cancel(); @@ -392,7 +426,7 @@ export class SuggestWidget implements IContentWidget, IDisposable { } if (!e.elements.length) { - this._lastSuggestionLabel = null; + this._ariaAlert(null); // TODO@Alex: Chromium bug // this.editor.setAriaActiveDescendant(null); @@ -401,10 +435,7 @@ export class SuggestWidget implements IContentWidget, IDisposable { } const item = e.elements[0]; - if (item.suggestion.label !== this._lastSuggestionLabel) { - this._lastSuggestionLabel = item.suggestion.label; - alert(nls.localize('ariaCurrentSuggestion',"{0}, suggestion", this._lastSuggestionLabel)); - } + this._ariaAlert(this._getSuggestionAriaAlertLabel(item)); // TODO@Alex: Chromium bug // // TODO@Alex: the list is not done rendering... @@ -433,6 +464,8 @@ export class SuggestWidget implements IContentWidget, IDisposable { this.list.setFocus(index); this.updateWidgetHeight(); this.list.reveal(index); + + this._ariaAlert(this._getSuggestionAriaAlertLabel(item)); }) .then(null, err => !isPromiseCanceledError(err) && onUnexpectedError(err)) .then(() => this.currentSuggestionDetails = null); @@ -485,6 +518,7 @@ export class SuggestWidget implements IContentWidget, IDisposable { hide(this.messageElement, this.listElement); show(this.details.element); this.show(); + this._ariaAlert(this.details.getAriaLabel()); break; }