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;
+ }
+ }
`;
}