// 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 ""; }