diff --git a/src/components/ha-code-editor-completion-items.ts b/src/components/ha-code-editor-completion-items.ts new file mode 100644 index 0000000000..3691bcc0c7 --- /dev/null +++ b/src/components/ha-code-editor-completion-items.ts @@ -0,0 +1,58 @@ +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; + +export interface CompletionItem { + label: string; + value: string; + subValue?: string; +} + +@customElement("ha-code-editor-completion-items") +export class HaCodeEditorCompletionItems extends LitElement { + @property({ attribute: false }) public items: CompletionItem[] = []; + + render() { + return this.items.map( + (item) => html` + ${item.label}: + ${item.value}${item.subValue && item.subValue.length > 0 + ? // prettier-ignore + html` (
${item.subValue}
)` + : nothing}
+ ` + ); + } + + static styles = css` + :host { + display: grid; + grid-template-columns: auto 1fr; + gap: 6px; + white-space: pre-wrap; + flex-wrap: nowrap; + } + + span { + display: flex; + align-items: center; + flex-flow: wrap; + word-wrap: break-word; + } + + pre { + margin: 0 3px; + padding: 3px; + background-color: var(--markdown-code-background-color, none); + border-radius: var(--ha-border-radius-sm, 4px); + line-height: var(--ha-line-height-condensed); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-code-editor-completion-items": HaCodeEditorCompletionItems; + } +} diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index 69387c89a3..f118156bef 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -1,6 +1,7 @@ import type { Completion, CompletionContext, + CompletionInfo, CompletionResult, CompletionSource, } from "@codemirror/autocomplete"; @@ -9,14 +10,17 @@ import type { EditorView, KeyBinding, ViewUpdate } from "@codemirror/view"; import { mdiArrowExpand, mdiArrowCollapse } from "@mdi/js"; import type { HassEntities } from "home-assistant-js-websocket"; import type { PropertyValues } from "lit"; -import { css, ReactiveElement } from "lit"; +import { css, ReactiveElement, html, render } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; import { stopPropagation } from "../common/dom/stop_propagation"; +import { getEntityContext } from "../common/entity/context/get_entity_context"; import type { HomeAssistant } from "../types"; +import type { CompletionItem } from "./ha-code-editor-completion-items"; import "./ha-icon"; import "./ha-icon-button"; +import "./ha-code-editor-completion-items"; declare global { interface HASSDomEvents { @@ -324,15 +328,72 @@ export class HaCodeEditor extends ReactiveElement { } }; + private _renderInfo = (completion: Completion): CompletionInfo => { + const key = completion.label; + const context = getEntityContext(this.hass!.states[key], this.hass!); + + const completionInfo = document.createElement("div"); + completionInfo.classList.add("completion-info"); + + const formattedState = this.hass!.formatEntityState(this.hass!.states[key]); + + const completionItems: CompletionItem[] = [ + { + label: this.hass!.localize( + "ui.components.entity.entity-state-picker.state" + ), + value: formattedState, + subValue: + // If the state exactly matches the formatted state, don't show the raw state + this.hass!.states[key].state === formattedState + ? undefined + : this.hass!.states[key].state, + }, + ]; + + if (context.device && context.device.name) { + completionItems.push({ + label: this.hass!.localize("ui.components.device-picker.device"), + value: context.device.name, + }); + } + + if (context.area && context.area.name) { + completionItems.push({ + label: this.hass!.localize("ui.components.area-picker.area"), + value: context.area.name, + }); + } + + if (context.floor && context.floor.name) { + completionItems.push({ + label: this.hass!.localize("ui.components.floor-picker.floor"), + value: context.floor.name, + }); + } + + render( + html` + + `, + completionInfo + ); + + return completionInfo; + }; + private _getStates = memoizeOne((states: HassEntities): Completion[] => { if (!states) { return []; } + const options = Object.keys(states).map((key) => ({ type: "variable", label: key, detail: states[key].attributes.friendly_name, - info: `State: ${states[key].state}`, + info: this._renderInfo, })); return options; @@ -615,6 +676,20 @@ export class HaCodeEditor extends ReactiveElement { top: calc(var(--safe-area-inset-top, 0px) + 8px); right: calc(var(--safe-area-inset-right, 0px) + 8px); } + + .completion-info { + display: grid; + gap: 3px; + padding: 8px; + } + + /* Hide completion info on narrow screens */ + @media (max-width: 600px) { + .cm-completionInfo, + .completion-info { + display: none; + } + } `; }