提交 e0969a51 编写于 作者: R Ramya Rao 提交者: GitHub

Toggle expanded docs, add stickyness to expansion (#26783)

* Toggle expanded docs, add stickyness to expansion

* New command for moving focus to/from details

* Store user choice in local storage

* Refactoring

* Show prev width for suggest list unless docs expand

* Add keybinding to Read Less button
上级 ecf1ca70
......@@ -9,15 +9,16 @@
width: 660px;
}
.monaco-editor .suggest-widget > .tree,
.monaco-editor .suggest-widget.docs-expanded > .tree,
.monaco-editor .suggest-widget > .details {
width: 330px;
}
.monaco-editor .suggest-widget.small,
.monaco-editor .suggest-widget.small > .tree,
.monaco-editor .suggest-widget > .tree,
.monaco-editor .suggest-widget.small.docs-expanded > .tree,
.monaco-editor .suggest-widget.small > .details {
width: 400px;
width: 430px;
}
.monaco-editor .suggest-widget.visible {
......@@ -72,6 +73,55 @@
font-weight: bold;
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .go-back,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .docs-details {
opacity: 0.6;
background-position: center center;
background-repeat: no-repeat;
background-size: 70%;
color: #0035DD;
cursor: pointer;
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .go-back {
background-image: url('./back.svg');
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .docs-details {
background-image: url('./info.svg');
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .go-back:hover,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .docs-details:hover {
opacity: 1;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label {
margin-left: 0.8em;
flex: 1;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
opacity: 0.7;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source {
display: inline;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .docs-details,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label,
.monaco-editor .suggest-widget.docs-expanded .monaco-list .monaco-list-row.focused > .contents > .main > .docs-details,
.monaco-editor .suggest-widget.docs-expanded .monaco-list .monaco-list-row.focused > .contents > .main > .type-label {
display: none;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .docs-details,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label,
.monaco-editor .suggest-widget.docs-expanded.small .monaco-list .monaco-list-row.focused > .contents > .main > .type-label {
display: inline;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon {
display: block;
height: 16px;
......@@ -141,9 +191,20 @@
white-space: pre-wrap;
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .type {
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header {
padding: 4px 5px;
display: flex;
box-sizing: border-box;
border-bottom: 1px solid rgba(204, 204, 204, 0.5);
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type {
flex: 2;
overflow: hidden;
text-overflow: ellipsis;
opacity: 0.7;
word-break: break-all;
margin: 0;
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p {
......@@ -155,12 +216,19 @@
display: none;
}
/* High Contrast Theming */
/* Dark theme */
.monaco-editor.hc-black .suggest-widget .details > .monaco-scrollable-element > .body > .docs {
color: #C07A7A;
.monaco-editor.vs-dark .suggest-widget .details > .monaco-scrollable-element > .body > .header {
border-color: rgba(85,85,85,0.5);
}
.monaco-editor.vs-dark .suggest-widget .details > .monaco-scrollable-element > .body > .header > .go-back,
.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .docs-details {
color: #4E94CE;
}
/* High Contrast Theming */
.monaco-editor.vs-dark .suggest-widget .monaco-list .monaco-list-row .icon,
.monaco-editor.hc-black .suggest-widget .monaco-list .monaco-list-row .icon { background-image: url('Misc_inverse_16x.svg'); }
......
......@@ -262,6 +262,12 @@ export class SuggestController implements IEditorContribution {
}
toggleSuggestionDetails(): void {
if (this._widget) {
this._widget.toggleDetails();
}
}
toggleSuggestionFocus(): void {
if (this._widget) {
this._widget.toggleDetailsFocus();
}
......@@ -408,3 +414,15 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
mac: { primary: KeyMod.WinCtrl | KeyCode.Space }
}
}));
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
id: 'toggleSuggestionFocus',
precondition: SuggestContext.Visible,
handler: x => x.toggleSuggestionFocus(),
kbOpts: {
weight: weight,
kbExpr: EditorContextKeys.textFocus,
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Space,
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Space }
}
}));
......@@ -13,7 +13,7 @@ import Event, { Emitter, chain } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition } from 'vs/base/browser/dom';
import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from 'vs/base/browser/dom';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IDelegate, IListEvent, IRenderer } from 'vs/base/browser/ui/list/list';
import { List } from 'vs/base/browser/ui/list/listWidget';
......@@ -30,6 +30,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder } from 'vs/platform/theme/common/colorRegistry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
const sticky = false; // for development purposes
......@@ -38,6 +39,8 @@ interface ISuggestionTemplateData {
icon: HTMLElement;
colorspan: HTMLElement;
highlightedLabel: HighlightedLabel;
typeLabel: HTMLElement;
documentationDetails: HTMLElement;
disposables: IDisposable[];
}
......@@ -66,15 +69,12 @@ function canExpandCompletionItem(item: ICompletionItem) {
class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
private triggerKeybindingLabel: string;
constructor(
private widget: SuggestWidget,
private editor: ICodeEditor,
@IKeybindingService keybindingService: IKeybindingService
private triggerKeybindingLabel: string
) {
const kb = keybindingService.lookupKeybinding('editor.action.triggerSuggest');
this.triggerKeybindingLabel = !kb ? '' : ` (${kb.getLabel()})`;
}
get templateId(): string {
......@@ -93,6 +93,11 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
const main = append(text, $('.main'));
data.highlightedLabel = new HighlightedLabel(main);
data.disposables.push(data.highlightedLabel);
data.typeLabel = append(main, $('span.type-label'));
data.documentationDetails = append(main, $('span.docs-details'));
data.documentationDetails.title = nls.localize('readMore', "Read More...{0}", this.triggerKeybindingLabel);
const configureFont = () => {
const configuration = this.editor.getConfiguration();
const fontFamily = configuration.fontInfo.fontFamily;
......@@ -106,6 +111,8 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
main.style.lineHeight = lineHeightPx;
data.icon.style.height = lineHeightPx;
data.icon.style.width = lineHeightPx;
data.documentationDetails.style.height = lineHeightPx;
data.documentationDetails.style.width = lineHeightPx;
};
configureFont();
......@@ -139,7 +146,24 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
}
data.highlightedLabel.set(suggestion.label, createMatches(element.matches));
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
if (canExpandCompletionItem(element)) {
show(data.documentationDetails);
data.documentationDetails.onmousedown = e => {
e.stopPropagation();
e.preventDefault();
};
data.documentationDetails.onclick = e => {
e.stopPropagation();
e.preventDefault();
this.widget.toggleDetails();
};
} else {
hide(data.documentationDetails);
data.documentationDetails.onmousedown = null;
data.documentationDetails.onclick = null;
}
}
......@@ -161,6 +185,8 @@ const enum State {
class SuggestionDetails {
private el: HTMLElement;
private back: HTMLElement;
private header: HTMLElement;
private scrollbar: DomScrollableElement;
private body: HTMLElement;
private type: HTMLElement;
......@@ -171,7 +197,8 @@ class SuggestionDetails {
constructor(
container: HTMLElement,
private widget: SuggestWidget,
private editor: ICodeEditor
private editor: ICodeEditor,
private triggerKeybindingLabel: string
) {
this.disposables = [];
......@@ -184,7 +211,12 @@ class SuggestionDetails {
append(this.el, this.scrollbar.getDomNode());
this.disposables.push(this.scrollbar);
this.type = append(this.body, $('p.type'));
this.header = append(this.body, $('.header'));
this.type = append(this.header, $('p.type'));
this.back = append(this.header, $('span.go-back'));
this.back.title = nls.localize('goback', "Read less...{0}", triggerKeybindingLabel);
this.docs = append(this.body, $('p.docs'));
this.ariaLabel = null;
......@@ -211,7 +243,17 @@ class SuggestionDetails {
this.type.innerText = item.suggestion.detail || '';
this.docs.textContent = item.suggestion.documentation;
this.el.style.height = this.type.clientHeight + this.docs.clientHeight + 'px';
this.el.style.height = this.type.clientHeight + this.docs.clientHeight + 8 + 'px';
this.back.onmousedown = e => {
e.preventDefault();
e.stopPropagation();
};
this.back.onclick = e => {
e.preventDefault();
e.stopPropagation();
this.widget.toggleDetails();
};
this.body.scrollTop = 0;
this.scrollbar.scanDomNode();
......@@ -251,10 +293,14 @@ class SuggestionDetails {
const configuration = this.editor.getConfiguration();
const fontFamily = configuration.fontInfo.fontFamily;
const fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize;
const lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight;
const fontSizePx = `${fontSize}px`;
const lineHeightPx = `${lineHeight}px`;
this.el.style.fontSize = fontSizePx;
this.type.style.fontFamily = fontFamily;
this.back.style.height = lineHeightPx;
this.back.style.width = lineHeightPx;
}
dispose(): void {
......@@ -308,18 +354,24 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
private readonly maxWidgetWidth = 660;
private readonly listWidth = 330;
private readonly minWidgetWidth = 400;
private readonly minWidgetWidth = 430;
private storageService: IStorageService;
constructor(
private editor: ICodeEditor,
@ITelemetryService private telemetryService: ITelemetryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService,
@IKeybindingService keybindingService: IKeybindingService
) {
const kb = keybindingService.lookupKeybinding('editor.action.triggerSuggest');
const triggerKeybindingLabel = !kb ? '' : ` (${kb.getLabel()})`;
this.isAuto = false;
this.focusedItem = null;
this.storageService = storageService;
this.element = $('.editor-widget.suggest-widget');
if (!this.editor.getConfiguration().contribInfo.iconsInSuggestions) {
......@@ -328,9 +380,9 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.messageElement = append(this.element, $('.message'));
this.listElement = append(this.element, $('.tree'));
this.details = new SuggestionDetails(this.element, this, this.editor);
this.details = new SuggestionDetails(this.element, this, this.editor, triggerKeybindingLabel);
let renderer: IRenderer<ICompletionItem, any> = instantiationService.createInstance(Renderer, this, this.editor);
let renderer: IRenderer<ICompletionItem, any> = instantiationService.createInstance(Renderer, this, this.editor, triggerKeybindingLabel);
this.list = new List(this.listElement, this, [renderer], {
useShadows: false,
......@@ -547,8 +599,11 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
this.show();
break;
case State.Open:
hide(this.messageElement);
show(this.listElement, this.details.element);
hide(this.messageElement, this.details.element);
show(this.listElement);
if (this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL, false)) {
show(this.details.element);
}
this.show();
break;
case State.Frozen:
......@@ -740,10 +795,26 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
}
}
toggleDetails(): void {
let expandDocs = this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL, false);
if (expandDocs) {
hide(this.details.element);
removeClass(this.element, 'docs-expanded');
} else {
show(this.details.element);
addClass(this.element, 'docs-expanded');
this.show();
}
this.storageService.store('expandSuggestionDocs', !expandDocs, StorageScope.GLOBAL);
}
showDetails(): void {
if (this.state !== State.Open && this.state !== State.Details) {
return;
}
if (this.storageService.getBoolean('expandSuggestionDocs', StorageScope.GLOBAL, false)) {
addClass(this.element, 'docs-expanded');
}
this.show();
this.editor.focus();
......@@ -792,7 +863,10 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
private updateWidgetHeight(): number {
let height = 0;
let maxSuggestionsToShow = this.editor.getLayoutInfo().contentWidth > this.minWidgetWidth ? 11 : 5;
let maxSuggestionsToShow = 11;
if (hasClass(this.element, 'small')) {
maxSuggestionsToShow = 5;
}
if (this.state === State.Empty || this.state === State.Loading) {
height = this.unfocusedHeight;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册