提交 4db0daca 编写于 作者: J Joao Moreno

parameter hints: first steps

related to #2330
上级 922dd9af
......@@ -4,13 +4,19 @@
*--------------------------------------------------------------------------------------------*/
.monaco-editor .parameter-hints-widget {
font-size: 12px;
width: 440px;
z-index: 10;
background-color: #F3F3F3;
border: 1px solid rgb(200, 200, 200);
/*padding: 0 0.5em 0 0.5em;*/
}
.monaco-editor.mac .parameter-hints-widget {
font-size: 11px;
.monaco-editor .parameter-hints-widget > .wrapper {
max-width: 440px;
}
.monaco-editor .parameter-hints-widget.multiple {
min-height: 3.3em;
padding: 0 0 0 1.9em;
}
.monaco-editor .parameter-hints-widget.visible {
......@@ -20,17 +26,27 @@
transition: left .05s ease-in-out;
}
.monaco-editor .parameter-hints-widget .wrapper {
position: relative;
bottom: 0;
border: 1px solid #ccc;
max-width: 440px;
padding: 0 0.5em 0 0.5em;
.monaco-editor .parameter-hints-widget p,
.monaco-editor .parameter-hints-widget ul {
margin: 8px 0;
}
.monaco-editor .parameter-hints-widget.multiple .wrapper {
min-height: 3.3em;
padding: 0 0.5em 0 1.9em;
.monaco-editor .parameter-hints-widget.multiple .body {
height: 100%;
border-left: 1px solid rgba(204, 204, 204, 0.5);
}
.monaco-editor .parameter-hints-widget .signature {
padding: 4px 5px;
}
.monaco-editor .parameter-hints-widget .signature.has-docs {
border-bottom: 1px solid rgba(204, 204, 204, 0.5);
}
.monaco-editor .parameter-hints-widget .docs {
padding: 0 5px;
font-size: 95%;
}
.monaco-editor .parameter-hints-widget .buttons {
......@@ -66,7 +82,6 @@
.monaco-editor .parameter-hints-widget .overloads {
position: absolute;
display: none;
font-size: 10px;
text-align: center;
bottom: 14px;
left: 0;
......@@ -80,10 +95,6 @@
display: block;
}
.monaco-editor .parameter-hints-widget .signatures {
overflow: hidden;
}
.monaco-editor .parameter-hints-widget .signature .parameter {
display: inline-block;
}
......@@ -93,12 +104,6 @@
text-decoration: underline;
}
.monaco-editor .parameter-hints-widget .documentation {
max-height: 3.2em;
overflow: hidden;
color: #A21B1B;
}
.monaco-editor .parameter-hints-widget .documentation-parameter > .parameter {
font-weight: bold;
margin-right: 0.5em;
......@@ -106,12 +111,16 @@
/*** VS Dark */
.monaco-editor.vs-dark .parameter-hints-widget .wrapper {
border-color: #707070;
.monaco-editor.vs-dark .parameter-hints-widget {
background-color: #2D2D30;
border-color: rgba(85,85,85,0.5);
}
.monaco-editor.vs-dark .parameter-hints-widget .documentation {
color: #C07A7A;
.monaco-editor.hc-black .parameter-hints-widget .body,
.monaco-editor.vs-dark .parameter-hints-widget .body,
.monaco-editor.hc-black .parameter-hints-widget .signature,
.monaco-editor.vs-dark .parameter-hints-widget .signature {
border-color: rgba(85,85,85,0.5);
}
.monaco-editor.hc-black .parameter-hints-widget .button.previous,
......@@ -126,10 +135,7 @@
/*** High Contrast */
.monaco-editor.hc-black .parameter-hints-widget .wrapper {
border-color: #707070;
}
.monaco-editor.hc-black .parameter-hints-widget .documentation {
color: #C07A7A;
}
.monaco-editor.hc-black .parameter-hints-widget {
background-color: #0C141F;
border: 2px solid #6FC3DF;
}
\ No newline at end of file
......@@ -15,10 +15,12 @@ import { SignatureHelp, SignatureInformation, SignatureHelpProviderRegistry } fr
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { RunOnceScheduler } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import Event, {Emitter} from 'vs/base/common/event';
import Event, {Emitter, filterEvent} from 'vs/base/common/event';
import {domEvent, stop} from 'vs/base/browser/event';
import { ICommonCodeEditor, ICursorSelectionChangedEvent } from 'vs/editor/common/editorCommon';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Context, provideSignatureHelp } from '../common/parameterHints';
import { IConfigurationChangedEvent } from 'vs/editor/common/editorCommon';
const $ = dom.$;
......@@ -151,11 +153,6 @@ export class ParameterHintsModel extends Disposable {
}
}
interface ISignatureView {
top: number;
height: number;
}
export class ParameterHintsWidget implements IContentWidget, IDisposable {
private static ID = 'editor.widget.parameterHintsWidget';
......@@ -164,12 +161,12 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
private keyVisible: IContextKey<boolean>;
private keyMultipleSignatures: IContextKey<boolean>;
private element: HTMLElement;
private signatures: HTMLElement;
private signature: HTMLElement;
private docs: HTMLElement;
private overloads: HTMLElement;
private signatureViews: ISignatureView[];
private currentSignature: number;
private visible: boolean;
private parameterHints: SignatureHelp;
private hints: SignatureHelp;
private announcedLabel: string;
private disposables: IDisposable[];
......@@ -183,12 +180,11 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
this.visible = false;
this.disposables = [];
this.disposables.push(this.model.onHint((e:IHintEvent) => {
this.disposables.push(this.model.onHint(e => {
this.show();
this.parameterHints = e.hints;
this.render(e.hints);
this.hints = e.hints;
this.currentSignature = e.hints.activeSignature;
this.select(this.currentSignature);
this.render();
}));
this.disposables.push(this.model.onCancel(() => {
......@@ -196,33 +192,28 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
}));
this.element = $('.editor-widget.parameter-hints-widget');
const wrapper = dom.append(this.element, $('.wrapper'));
this.disposables.push(dom.addDisposableListener(this.element, 'click', () => {
this.next();
this.editor.focus();
}));
const onClick = stop(domEvent(this.element, 'click'));
onClick(this.next, this, this.disposables);
const wrapper = dom.append(this.element, $('.wrapper.monaco-editor-background'));
const buttons = dom.append(wrapper, $('.buttons'));
const previous = dom.append(buttons, $('.button.previous'));
const next = dom.append(buttons, $('.button.next'));
this.disposables.push(dom.addDisposableListener(previous, 'click', e => {
e.preventDefault();
e.stopPropagation();
this.previous();
}));
const onPreviousClick = stop(domEvent(previous, 'click'));
onPreviousClick(this.previous, this, this.disposables);
this.disposables.push(dom.addDisposableListener(next, 'click', e => {
e.preventDefault();
e.stopPropagation();
this.next();
}));
const onNextClick = stop(domEvent(next, 'click'));
onNextClick(this.next, this, this.disposables);
this.overloads = dom.append(wrapper, $('.overloads'));
this.signatures = dom.append(wrapper, $('.signatures'));
this.signatureViews = [];
const body = dom.append(wrapper, $('.body'));
this.signature = dom.append(body, $('.signature'));
this.docs = dom.append(body, $('.docs'));
this.currentSignature = 0;
this.editor.addContentWidget(this);
......@@ -233,6 +224,19 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
this.editor.layoutContentWidget(this);
}
}));
const updateFont = () => {
const fontInfo = this.editor.getConfiguration().fontInfo;
this.element.style.fontSize = `${fontInfo.fontSize}px`;
};
updateFont();
const onFontInfo = filterEvent(this.editor.onDidChangeConfiguration.bind(this.editor), (e: IConfigurationChangedEvent) => e.fontInfo);
onFontInfo(updateFont, null, this.disposables);
this.disposables.push(this.editor.onDidLayoutChange(e => this.updateMaxHeight()));
this.updateMaxHeight();
}
private show(): void {
......@@ -253,7 +257,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
this.keyVisible.reset();
this.visible = false;
this.parameterHints = null;
this.hints = null;
this.announcedLabel = null;
dom.removeClass(this.element, 'visible');
this.editor.layoutContentWidget(this);
......@@ -269,61 +273,68 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
return null;
}
private render(hints:SignatureHelp): void {
if (hints.signatures.length > 1) {
dom.addClass(this.element, 'multiple');
} else {
dom.removeClass(this.element, 'multiple');
}
private render(): void {
const multiple = this.hints.signatures.length > 1;
dom.toggleClass(this.element, 'multiple', multiple);
this.keyMultipleSignatures.set(multiple);
this.signatures.innerHTML = '';
this.signatureViews = [];
let height = 0;
this.signature.innerHTML = '';
this.docs.innerHTML = '';
for (let i = 0, len = hints.signatures.length; i < len; i++) {
const signature = hints.signatures[i];
const signatureElement = this.renderSignature(this.signatures, signature, hints.activeParameter);
const signature = this.hints.signatures[this.currentSignature];
this.renderDocumentation(signatureElement, signature, hints.activeParameter);
const code = dom.append(this.signature, $('.code'));
const hasParameters = signature.parameters.length > 0;
const signatureHeight = dom.getContentHeight(signatureElement);
const fontInfo = this.editor.getConfiguration().fontInfo;
code.style.fontSize = `${fontInfo.fontSize}px`;
code.style.fontFamily = fontInfo.fontFamily;
this.signatureViews.push({
top: height,
height: signatureHeight
});
if(!hasParameters) {
const label = dom.append(code, $('span'));
label.textContent = signature.label;
height += signatureHeight;
} else {
this.renderParameters(code, signature, this.hints.activeParameter);
}
this.keyMultipleSignatures.set(this.signatureViews.length > 1);
}
const activeParameter = signature.parameters[this.hints.activeParameter];
private applyFont(element: HTMLElement): void {
const fontInfo = this.editor.getConfiguration().fontInfo;
element.style.fontFamily = fontInfo.fontFamily;
}
if(activeParameter && activeParameter.documentation) {
const documentation = $('span.documentation');
documentation.textContent = activeParameter.documentation;
dom.append(this.docs, $('p', null, documentation));
}
private renderSignature(element: HTMLElement, signature:SignatureInformation, currentParameter:number): HTMLElement {
const signatureElement = dom.append(element, $('.signature'));
const code = dom.append(signatureElement, $('.code'));
const hasParameters = signature.parameters.length > 0;
dom.toggleClass(this.signature, 'has-docs', !!signature.documentation);
this.applyFont(code);
if(signature.documentation) {
dom.append(this.docs, $('p', null, signature.documentation));
}
if(!hasParameters) {
const label = dom.append(code, $('span'));
label.textContent = signature.label;
let currentOverload = String(this.currentSignature + 1);
} else {
this.renderParameters(code, signature, currentParameter);
if (this.hints.signatures.length < 10) {
currentOverload += `/${this.hints.signatures.length}`;
}
this.overloads.textContent = currentOverload;
if (activeParameter) {
const labelToAnnounce = activeParameter.label;
// Select method gets called on every user type while parameter hints are visible.
// We do not want to spam the user with same announcements, so we only announce if the current parameter changed.
if (this.announcedLabel !== labelToAnnounce) {
aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce));
this.announcedLabel = labelToAnnounce;
}
}
return signatureElement;
this.editor.layoutContentWidget(this);
}
private renderParameters(parent: HTMLElement, signature: SignatureInformation, currentParameter: number): void {
let end = signature.label.length;
let idx = 0;
let element: HTMLSpanElement;
......@@ -357,88 +368,62 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
element = document.createElement('span');
element.textContent = signature.label.substring(0, end);
dom.prepend(parent, element);
}
private renderDocumentation(element: HTMLElement, signature:SignatureInformation, activeParameterIdx:number): void {
if(signature.documentation) {
const documentation = $('.documentation');
documentation.textContent = signature.documentation;
dom.append(element, documentation);
}
const activeParameter = signature.parameters[activeParameterIdx];
if(activeParameter && activeParameter.documentation) {
const parameter = $('.documentation-parameter');
const label = $('span.parameter');
this.applyFont(label);
label.textContent = activeParameter.label;
// private select(position: number): void {
// const signature = this.signatureViews[position];
const documentation = $('span.documentation');
documentation.textContent = activeParameter.documentation;
// if (!signature) {
// return;
// }
dom.append(parameter, label);
dom.append(parameter, documentation);
dom.append(element, parameter);
}
}
// this.signatures.style.height = `${ signature.height }px`;
// this.signatures.scrollTop = signature.top;
private select(position: number): void {
const signature = this.signatureViews[position];
// let overloads = '' + (position + 1);
if (!signature) {
return;
}
// if (this.signatureViews.length < 10) {
// overloads += '/' + this.signatureViews.length;
// }
this.signatures.style.height = `${ signature.height }px`;
this.signatures.scrollTop = signature.top;
// this.overloads.textContent = overloads;
let overloads = '' + (position + 1);
// if (this.hints && this.hints.signatures[position].parameters[this.hints.activeParameter]) {
// const labelToAnnounce = this.hints.signatures[position].parameters[this.hints.activeParameter].label;
// // Select method gets called on every user type while parameter hints are visible.
// // We do not want to spam the user with same announcements, so we only announce if the current parameter changed.
// if (this.announcedLabel !== labelToAnnounce) {
// aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce));
// this.announcedLabel = labelToAnnounce;
// }
// }
if (this.signatureViews.length < 10) {
overloads += '/' + this.signatureViews.length;
}
this.overloads.textContent = overloads;
if (this.parameterHints && this.parameterHints.signatures[position].parameters[this.parameterHints.activeParameter]) {
const labelToAnnounce = this.parameterHints.signatures[position].parameters[this.parameterHints.activeParameter].label;
// Select method gets called on every user type while parameter hints are visible.
// We do not want to spam the user with same announcements, so we only announce if the current parameter changed.
if (this.announcedLabel !== labelToAnnounce) {
aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce));
this.announcedLabel = labelToAnnounce;
}
}
this.editor.layoutContentWidget(this);
}
// this.editor.layoutContentWidget(this);
// }
next(): boolean {
if (this.signatureViews.length < 2) {
const length = this.hints.signatures.length;
if (length < 2) {
this.cancel();
return false;
}
this.currentSignature = (this.currentSignature + 1) % this.signatureViews.length;
this.select(this.currentSignature);
this.currentSignature = (this.currentSignature + 1) % length;
this.render();
return true;
}
previous(): boolean {
if (this.signatureViews.length < 2) {
const length = this.hints.signatures.length;
if (length < 2) {
this.cancel();
return false;
}
this.currentSignature--;
if (this.currentSignature < 0) {
this.currentSignature = this.signatureViews.length - 1;
}
this.select(this.currentSignature);
this.currentSignature = (this.currentSignature - 1 + length) % length;
this.render();
return true;
}
......@@ -458,6 +443,11 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
this.model.trigger(0);
}
private updateMaxHeight(): void {
const height = Math.max(this.editor.getLayoutInfo().height / 4, 250);
this.element.style.maxHeight = `${ height }px`;
}
dispose(): void {
this.disposables = dispose(this.disposables);
this.model = null;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册