• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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