1// Copyright 2022 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 {CompletionContext} from '@codemirror/autocomplete' 16import {syntaxTree} from '@codemirror/language' 17import {Device} from "pigweedjs"; 18 19const completePropertyAfter = ['PropertyName', '.', '?.'] 20const dontCompleteIn = [ 21 'TemplateString', 22 'LineComment', 23 'BlockComment', 24 'VariableDefinition', 25 'PropertyDefinition' 26] 27var objectPath = require("object-path"); 28 29export function completeFromGlobalScope(context: CompletionContext) { 30 let nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1) 31 32 if ( 33 completePropertyAfter.includes(nodeBefore.name) && 34 nodeBefore.parent?.name == 'MemberExpression' 35 ) { 36 let object = nodeBefore.parent.getChild('Expression') 37 if (object?.name == 'VariableName') { 38 let from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from 39 let variableName = context.state.sliceDoc(object.from, object.to) 40 // @ts-ignore 41 if (typeof window[variableName] == 'object') { 42 // @ts-ignore 43 return completeProperties(from, window[variableName]) 44 } 45 } 46 else if (object?.name == 'MemberExpression') { 47 let from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from 48 let variableName = context.state.sliceDoc(object.from, object.to) 49 let variable = resolveWindowVariable(variableName); 50 // @ts-ignore 51 if (typeof variable == 'object') { 52 // @ts-ignore 53 return completeProperties(from, variable, variableName) 54 } 55 } 56 } else if (nodeBefore.name == 'VariableName') { 57 return completeProperties(nodeBefore.from, window) 58 } else if (context.explicit && !dontCompleteIn.includes(nodeBefore.name)) { 59 return completeProperties(context.pos, window) 60 } 61 return null 62} 63 64function completeProperties(from: number, object: Object, variableName?: string) { 65 let options = [] 66 for (let name in object) { 67 // @ts-ignore 68 if (object[name] instanceof Function && variableName) { 69 debugger; 70 options.push({ 71 label: name, 72 // @ts-ignore 73 detail: getFunctionDetailText(`${variableName}.${name}`), 74 type: 'function' 75 }) 76 } 77 else { 78 options.push({ 79 label: name, 80 type: 'variable' 81 }) 82 } 83 84 } 85 return { 86 from, 87 options, 88 validFor: /^[\w$]*$/ 89 } 90} 91 92function resolveWindowVariable(variableName: string) { 93 if (objectPath.has(window, variableName)) { 94 return objectPath.get(window, variableName); 95 } 96} 97 98function getFunctionDetailText(fullExpression: string): string { 99 if (fullExpression.startsWith("device.rpcs.")) { 100 fullExpression = fullExpression.replace("device.rpcs.", ""); 101 } 102 const args = ((window as any).device as Device).getMethodArguments(fullExpression); 103 if (args) { 104 return `(${args.join(", ")})`; 105 } 106 return ""; 107} 108