aboutsummaryrefslogtreecommitdiff
path: root/pw_web/webconsole/components/repl/autocomplete.ts
diff options
context:
space:
mode:
Diffstat (limited to 'pw_web/webconsole/components/repl/autocomplete.ts')
-rw-r--r--pw_web/webconsole/components/repl/autocomplete.ts107
1 files changed, 107 insertions, 0 deletions
diff --git a/pw_web/webconsole/components/repl/autocomplete.ts b/pw_web/webconsole/components/repl/autocomplete.ts
new file mode 100644
index 000000000..24d7efb3a
--- /dev/null
+++ b/pw_web/webconsole/components/repl/autocomplete.ts
@@ -0,0 +1,107 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import {CompletionContext} from '@codemirror/autocomplete'
+import {syntaxTree} from '@codemirror/language'
+import {Device} from "pigweedjs";
+
+const completePropertyAfter = ['PropertyName', '.', '?.']
+const dontCompleteIn = [
+ 'TemplateString',
+ 'LineComment',
+ 'BlockComment',
+ 'VariableDefinition',
+ 'PropertyDefinition'
+]
+var objectPath = require("object-path");
+
+export function completeFromGlobalScope(context: CompletionContext) {
+ let nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1)
+
+ if (
+ completePropertyAfter.includes(nodeBefore.name) &&
+ nodeBefore.parent?.name == 'MemberExpression'
+ ) {
+ let object = nodeBefore.parent.getChild('Expression')
+ if (object?.name == 'VariableName') {
+ let from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from
+ let variableName = context.state.sliceDoc(object.from, object.to)
+ // @ts-ignore
+ if (typeof window[variableName] == 'object') {
+ // @ts-ignore
+ return completeProperties(from, window[variableName])
+ }
+ }
+ else if (object?.name == 'MemberExpression') {
+ let from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from
+ let variableName = context.state.sliceDoc(object.from, object.to)
+ let variable = resolveWindowVariable(variableName);
+ // @ts-ignore
+ if (typeof variable == 'object') {
+ // @ts-ignore
+ return completeProperties(from, variable, variableName)
+ }
+ }
+ } else if (nodeBefore.name == 'VariableName') {
+ return completeProperties(nodeBefore.from, window)
+ } else if (context.explicit && !dontCompleteIn.includes(nodeBefore.name)) {
+ return completeProperties(context.pos, window)
+ }
+ return null
+}
+
+function completeProperties(from: number, object: Object, variableName?: string) {
+ let options = []
+ for (let name in object) {
+ // @ts-ignore
+ if (object[name] instanceof Function && variableName) {
+ debugger;
+ options.push({
+ label: name,
+ // @ts-ignore
+ detail: getFunctionDetailText(`${variableName}.${name}`),
+ type: 'function'
+ })
+ }
+ else {
+ options.push({
+ label: name,
+ type: 'variable'
+ })
+ }
+
+ }
+ return {
+ from,
+ options,
+ validFor: /^[\w$]*$/
+ }
+}
+
+function resolveWindowVariable(variableName: string) {
+ if (objectPath.has(window, variableName)) {
+ return objectPath.get(window, variableName);
+ }
+}
+
+function getFunctionDetailText(fullExpression: string): string {
+ if (fullExpression.startsWith("device.rpcs.")) {
+ fullExpression = fullExpression.replace("device.rpcs.", "");
+ }
+ const args = ((window as any).device as Device).getMethodArguments(fullExpression);
+ if (args) {
+ return `(${args.join(", ")})`;
+ }
+ return "";
+}