diff options
Diffstat (limited to 'pw_web/webconsole/components/repl/autocomplete.ts')
-rw-r--r-- | pw_web/webconsole/components/repl/autocomplete.ts | 107 |
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 ""; +} |