xref: /aosp_15_r20/external/pigweed/pw_web/log-viewer/src/components/repl/repl.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15import { LitElement, html, PropertyValues } from 'lit';
16import { customElement, query, state } from 'lit/decorators.js';
17import { EvalOutput, ReplKernel } from '../../repl-kernel';
18import './code-editor';
19import { CodeEditor } from './code-editor';
20import { styles } from './repl.styles';
21import { themeDark } from '../../themes/dark';
22import { themeLight } from '../../themes/light';
23
24@customElement('repl-component')
25export class Repl extends LitElement {
26  static styles = [themeDark, themeLight, styles];
27  private codeEditor: CodeEditor;
28  private evalResults: EvalOutput[] = [];
29  private readonly LOCALSTORAGE_KEY = 'replHistory';
30
31  // prettier-ignore
32  private welcomeMsg = html`Welcome to the Pigweed Web Console!
33
34  Input keyboard shortcuts:
35    Press <kbd>Enter</kbd> to run
36    Press <kbd>Shift</kbd> + <kbd>Enter</kbd> to create multi-line commands
37    Press <kbd>Up</kbd> or <kbd>Down</kbd> to navigate command history
38
39  Example Python commands:
40    device.rpcs.pw.rpc.EchoService.Echo(msg='hello!')
41    LOG.warning('Message appears in Host Logs window.')
42    DEVICE_LOG.warning('Message appears in Device Logs window.')`;
43
44  @query('#output') _output!: HTMLElement;
45
46  @state()
47  replTitle: string;
48
49  constructor(
50    private replKernel: ReplKernel,
51    title = 'REPL',
52  ) {
53    super();
54    this.replTitle = title;
55
56    this.evalResults.push({ result: this.welcomeMsg });
57    this.codeEditor = new CodeEditor(
58      '',
59      this.replKernel.autocomplete.bind(this.replKernel),
60      async (code: string) => {
61        const output = await this.replKernel.eval(code);
62        this.evalResults.push({ stdin: code, ...output });
63        this.requestUpdate();
64      },
65    );
66  }
67
68  firstUpdated() {
69    // Append the code-editor instance to the shadow DOM
70    this.shadowRoot!.querySelector('#editor')!.appendChild(this.codeEditor);
71  }
72
73  protected updated(_changedProperties: PropertyValues): void {
74    if (this._output.scrollHeight > this._output.clientHeight) {
75      this._output.scrollTop = this._output.scrollHeight;
76    }
77  }
78
79  /** Remove all results from output */
80  private clearOutput() {
81    this.evalResults = [];
82    this.requestUpdate();
83  }
84
85  render() {
86    return html`
87      <div id="repl">
88        <div class="header">
89          ${this.replTitle}
90          <span class="actions-container">
91            <md-icon-button
92              @click=${this.clearOutput}
93              title="Clear Repl output"
94            >
95              <md-icon>&#xe16c;</md-icon>
96            </md-icon-button>
97            <md-icon-button
98              href="https://pigweed.dev/pw_web/repl.html#python-shell"
99              title="Go to the python shell documentation page"
100            >
101              <md-icon>&#xe8fd;</md-icon>
102            </md-icon-button>
103          </span>
104        </div>
105        <ul id="output">
106          ${this.evalResults.map(
107            (result) => html`
108              <li>
109                ${!result.stderr &&
110                !result.stdout &&
111                !result.result &&
112                !result.stdin
113                  ? html`<span class="stdout"><i>No output</i></span>`
114                  : ''}
115                <span class="stdin">${result.stdin}</span>
116                <span class="stderr">${result.stderr}</span>
117                <span class="stdout">${result.stdout}</span>
118                <span class="stdout">${result.result}</span>
119              </li>
120            `,
121          )}
122        </ul>
123        <div id="editor"></div>
124      </div>
125    `;
126  }
127}
128