System.register(['reactive-jsx', 'utils', 'src/client/constants.js', './code-mirror-utils.js', 'src/external/color-hash.js'], function (_export, _context) {
  "use strict";

  var _childElement, _childText, _attributeExpression, _childExpression, _children, _attributeStringLiteral, _attributes, _element, uniq, javaScriptKeywords, cssProperties, domEvents, utils, indentSelections, ColorHash;

  return {
    setters: [function (_reactiveJsx) {
      _childElement = _reactiveJsx.childElement;
      _childText = _reactiveJsx.childText;
      _attributeExpression = _reactiveJsx.attributeExpression;
      _childExpression = _reactiveJsx.childExpression;
      _children = _reactiveJsx.children;
      _attributeStringLiteral = _reactiveJsx.attributeStringLiteral;
      _attributes = _reactiveJsx.attributes;
      _element = _reactiveJsx.element;
    }, function (_utils) {
      uniq = _utils.uniq;
      utils = _utils;
    }, function (_srcClientConstantsJs) {
      javaScriptKeywords = _srcClientConstantsJs.javaScriptKeywords;
      cssProperties = _srcClientConstantsJs.cssProperties;
      domEvents = _srcClientConstantsJs.domEvents;
    }, function (_codeMirrorUtilsJs) {
      indentSelections = _codeMirrorUtilsJs.indentSelections;
    }, function (_srcExternalColorHashJs) {
      ColorHash = _srcExternalColorHashJs.default;
    }],
    execute: function () {
      const __SystemJSRewritingHack = {};
      _recorder_._src_components_widgets_lively_code_mirror_hint_js = _recorder_._src_components_widgets_lively_code_mirror_hint_js || {};
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'javascriptHint', {
        get() {
          return javascriptHint;
        },

        set(thisIsVererySecretVariableName) {
          javascriptHint = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'forOrigin', {
        get() {
          return forOrigin;
        },

        set(thisIsVererySecretVariableName) {
          forOrigin = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'colorForString', {
        get() {
          return colorForString;
        },

        set(thisIsVererySecretVariableName) {
          colorForString = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'searchImports', {
        get() {
          return searchImports;
        },

        set(thisIsVererySecretVariableName) {
          searchImports = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'lcmFromCM', {
        get() {
          return lcmFromCM;
        },

        set(thisIsVererySecretVariableName) {
          lcmFromCM = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'uniq', {
        get() {
          return uniq;
        },

        set(thisIsVererySecretVariableName) {
          uniq = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });


      var stringProps = Object.getOwnPropertyNames(''.__proto__);
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'stringProps', {
        get() {
          return stringProps;
        },

        set(thisIsVererySecretVariableName) {
          stringProps = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      var arrayProps = Object.getOwnPropertyNames([].__proto__);
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'arrayProps', {
        get() {
          return arrayProps;
        },

        set(thisIsVererySecretVariableName) {
          arrayProps = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      var funcProps = Object.getOwnPropertyNames(function () {}.__proto__);
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'funcProps', {
        get() {
          return funcProps;
        },

        set(thisIsVererySecretVariableName) {
          funcProps = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'domEvents', {
        get() {
          return domEvents;
        },

        set(thisIsVererySecretVariableName) {
          domEvents = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'cssProperties', {
        get() {
          return cssProperties;
        },

        set(thisIsVererySecretVariableName) {
          cssProperties = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'javaScriptKeywords', {
        get() {
          return javaScriptKeywords;
        },

        set(thisIsVererySecretVariableName) {
          javaScriptKeywords = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'utils', {
        get() {
          return utils;
        },

        set(thisIsVererySecretVariableName) {
          utils = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'indentSelections', {
        get() {
          return indentSelections;
        },

        set(thisIsVererySecretVariableName) {
          indentSelections = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });


      var Pos = CodeMirror.Pos;

      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'Pos', {
        get() {
          return Pos;
        },

        set(thisIsVererySecretVariableName) {
          Pos = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      function lcmFromCM(cm) {
        return lively.allParents(cm.getWrapperElement(), undefined, true).find(e => e.tagName === 'LIVELY-CODE-MIRROR');
      }

      /*MD # Import MD*/
      var importsCache = _recorder_._src_components_widgets_lively_code_mirror_hint_js.importsCache;
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'importsCache', {
        get() {
          return importsCache;
        },

        set(thisIsVererySecretVariableName) {
          importsCache = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      var importsCacheTime = _recorder_._src_components_widgets_lively_code_mirror_hint_js.importsCacheTime;
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'importsCacheTime', {
        get() {
          return importsCacheTime;
        },

        set(thisIsVererySecretVariableName) {
          importsCacheTime = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      async function searchImports() {
        if (!importsCache || Date.now() - importsCacheTime > 60 * 60 * 1000) {
          _recorder_._src_components_widgets_lively_code_mirror_hint_js.importsCacheTime = Date.now();
          _recorder_._src_components_widgets_lively_code_mirror_hint_js.importsCache = await fetch(lively4url + "/../_search/files", {
            headers: {
              searchpattern: "^import",
              rootdirs: lively4url.replace(/.*\//, ""),
              excludes: "node_modules,src/external,vendor/,lodash-bound.js"
            } }).then(r => r.text());
        }
        return importsCache;
      }
      searchImports();

      /*MD # Completions MD*/
      class Completions {

        constructor(cm) {
          this.cm = cm;
          this.list = [];
          this.seenCompletions = new Set();
          this.prepareToken();
        }

        prepareToken() {
          const cm = this.cm;

          // Find the token at the cursor
          var cursor = cm.getCursor();
          var token = cm.getTokenAt(cursor);

          token.state = CodeMirror.innerMode(cm.getMode(), token.state).state;

          // lively.notify(token.string, token.type);
          if (token.type === 'string') {
            token = {
              start: token.start + 1,
              end: cursor.ch,
              string: token.string.substring(1, cursor.ch - token.start),
              state: token.state,
              type: 'string'
            };
          } else if (!/^[\w$_]*$/.test(token.string)) {
            // If it's not a 'word-style' token, ignore the token.
            token = {
              start: cursor.ch,
              end: cursor.ch,
              string: "",
              state: token.state,
              type: token.string == "." ? "property" : null
            };
          } else if (token.end > cursor.ch) {
            token.end = cursor.ch;
            token.string = token.string.slice(0, cursor.ch - token.start);
          }

          this.token = token;
        }

        maybeAdd(completion) {
          const simple = typeof completion === 'string';
          const text = simple ? completion : completion.text;

          if (this.seenCompletions.has(text)) {
            return;
          }

          // #TODO: getTextAt(from, to) or string iff no from/to in completion
          if (!text.startsWith(this.token.string)) {
            return;
          }

          this.seenCompletions.add(text);
          this.list.push(completion);
        }

        asCompletionsObject() {
          if (this.list.length === 0) {
            return;
          }

          const line = this.cm.getCursor().line;
          const completionsObject = {
            list: this.list,
            from: Pos(line, this.token.start),
            to: Pos(line, this.token.end)
          };

          CodeMirror.on(completionsObject, "shown", () => {
            // lively.warn("shown");
          });
          CodeMirror.on(completionsObject, "select", (completion, element) => {
            if (!element.parentElement.querySelector('li.shortcut-present')) {
              element.classList.add('shortcut-present');
              const hints = element.parentElement.querySelectorAll('li.CodeMirror-hint');
              hints.forEach((hint, i) => {
                if (i > 9) {
                  return;
                }
                hint.prepend(_element('span', _attributes(_attributeStringLiteral('style', 'position: absolute; transform: translate(-6px, 0); font-size: xx-small; color: darkgray; background: white;')), _children(_childExpression(i)), {
                  file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                  end: {
                    column: 155,
                    line: 119
                  },
                  start: {
                    column: 23,
                    line: 119
                  }
                }));
              });
            }
          });
          CodeMirror.on(completionsObject, "pick", completion => {
            // lively.warn("pick");
          });
          CodeMirror.on(completionsObject, "close", () => {
            // lively.warn("close");
          });

          return completionsObject;
        }
      }
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'Completions', {
        get() {
          return Completions;
        },

        set(thisIsVererySecretVariableName) {
          Completions = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'ColorHash', {
        get() {
          return ColorHash;
        },

        set(thisIsVererySecretVariableName) {
          ColorHash = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });

      const colorHash = new ColorHash({ saturation: 0.8, lightness: 0.8 });

      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'colorHash', {
        get() {
          return colorHash;
        },

        set(thisIsVererySecretVariableName) {
          colorHash = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      function colorForString(str) {
        return colorHash.hex(str);
      }

      function forOrigin(completion, origin) {
        if (typeof completion === 'string') {
          completion = {
            text: completion
          };
        }

        return Object.assign({
          render(element, self, data) {
            if (completion.innerRender) {
              completion.innerRender(element, self, data);
            } else {
              element.append(_element('span', _attributes(), _children(_childExpression(completion.displayText || completion.text)), {
                file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                end: {
                  column: 79,
                  line: 152
                },
                start: {
                  column: 23,
                  line: 152
                }
              }));
            }
            element.append(_element('span', _attributes(_attributeStringLiteral('style', 'display: inline-block; width: 50px;')), _children(), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
              end: {
                column: 78,
                line: 154
              },
              start: {
                column: 21,
                line: 154
              }
            }));
            element.append(_element('span', _attributes(_attributeExpression('style', `background: ${colorForString(origin)}; color: black; position: absolute; right: 2px; font-size: x-small;`)), _children(_childExpression(origin)), {
              file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
              end: {
                column: 157,
                line: 155
              },
              start: {
                column: 21,
                line: 155
              }
            }));
          }
        }, completion);
      }

      /*MD # CompletionsBuilder MD*/
      class CompletionsBuilder {

        constructor(cm) {
          this.cm = cm;
          this.completions = new Completions(cm);
        }

        maybeAdd(str) {
          this.completions.maybeAdd(str);
        }

        async buildHint(options) {
          await this.collectCompletions(options);

          // lively.notify(this.completions.list.length, 'num completions');
          return this.completions.asCompletionsObject();
        }

        async collectCompletions(options) {
          const cm = this.cm;
          const completions = this.completions;

          var cursor = cm.getCursor();

          const line = cm.getLine(cursor.line);
          if (line.startsWith("import")) {
            await this.completeFromImport(cm);
            return;
          }

          var token = completions.token;
          // lively.notify('type: ' + token.type, 'string: ' + token.string);

          if (token.type === 'string') {
            cssProperties.map(property => forOrigin(property, 'cssProperties')).forEach(this.maybeAdd.bind(this));
            domEvents.map(property => forOrigin(property, 'domEvents')).forEach(this.maybeAdd.bind(this));
            return;
          }

          if (token.type === 'comment') {
            return;
          }

          this.completeLively();
          this.completeDoItCommand();

          await this.completeFromTern();

          if (token.type === 'property') {
            const variables = [];
            const startExpr = {};

            let lineNumber = cursor.line;
            let firstLine = true;
            whileLoop: while (lineNumber >= 0) {
              let tokens;
              tokens = cm.getLineTokens(lineNumber, true);
              if (firstLine) {
                firstLine = false;
                tokens = tokens.filter(t => t.start < token.start);
              }
              tokens = tokens.reverse();

              let parensBalance = 0;

              for (let { string, type, start } of tokens) {
                // console.log( string, type, start )
                if (type === null && string === '(') {
                  parensBalance--;
                } else if (type === null && string === ')') {
                  parensBalance++;
                } else if (typeof type === 'string' && type.includes('variable')) {
                  variables.push(string);
                  if (parensBalance <= 0) {
                    startExpr.ch = start;
                    startExpr.line = lineNumber;
                    break whileLoop;
                  }
                }
              }
              lineNumber--;
            }

            let code;
            // lively.notify(variables, 'variables')
            try {
              code = cm.getRange(startExpr, { line: cursor.line, ch: token.start - 1 });
            } catch (e) {
              console.warn("Error in code-mirror-hint, could not comlete, because: ", e);
              return;
            }

            const decls = [];

            // lively.getGlobalBounds(document.body)
            //   .topLeft().subPt(pt(-10, 0)).x.rFOOOOOO

            const uniqueVariables = new Set(variables);

            decls.push(`var decls = await System.import('src/components/widgets/lively-code-mirror-hint-decls.js');`);
            const codeMirrorDecls = 'cm, lcm';
            if (['cm', 'lcm'].some(vari => uniqueVariables.has(vari))) {
              decls.push(`var { cm, lcm } = await decls.codeMirror();`);
            }
            const babelDecls = 'babel, t, template, path, parentPath, node, parentNode, identifier, scope, binding';
            if (babelDecls.split(', ').some(vari => uniqueVariables.has(vari))) {
              decls.push(`var { ${babelDecls} } = await decls.babel();`);
            }
            const finalCode = `(async () => {
try {
${decls.join(`;
`)}
return ${code}
} catch(e) {}
})()`;

            var { value, isError } = await options.codemirror.boundEval(finalCode);
            if (isError) {
              lively.warn(value);
            } else {
              value = await value;
              this.gatherCompletionsForObject(value, 'x');
            }
          }

          var tprop = token;
          const getToken = cm.getTokenAt.bind(cm);
          // If it is a property, find out what it is a property of.
          while (tprop.type == "property") {
            tprop = getToken(Pos(cursor.line, tprop.start));
            if (tprop.string != ".") {
              break;
            }
            tprop = getToken(Pos(cursor.line, tprop.start));
            if (!context) {
              var context = [];
            }
            context.push(tprop);
          }

          if (context && context.length === 1 && context[0].string === 'this') {
            this.completeMembersOfThis();
          }

          var global = options && options.globalScope || window;

          if (context && context.length) {
            // If this is a property, see if it belongs to some object we can
            // find in the current environment.
            var obj = context.pop(),
                base;

            if (obj.type && obj.type.indexOf("variable") === 0 || obj.string == "this") {
              if (options && options.additionalContext) base = options.additionalContext[obj.string];

              if (base === undefined && obj && obj.string) {
                // #TODO instead of doing a blackbox evaluation of a string, we could look into the various scopes... 
                // in the module or live prgoramming data structures... e.g. from AST with trace data such as #ContinuousEditor
                // documentation: getDoitContext() and getTargetModule()
                var { value, isError } = await options.codemirror.boundEval(obj.string);
                if (!isError) {
                  base = value;
                }
              }
              if (!options || options.useGlobalScope !== false) {
                base = base || global[obj.string];
              }
            } else if (obj.type == "string") {
              base = "";
            } else if (obj.type == "atom") {
              base = 1;
            } else if (obj.type == "function") {

              // #TODO make it work for function calls such as pt(10,10), too.... 
              if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && typeof global.jQuery == 'function') {
                base = global.jQuery();
              } else if (global._ != null && obj.string == '_' && typeof global._ == 'function') {
                base = global._();
              };
            }
            while (base != null && context.length) base = base[context.pop().string];
            if (base != null) this.gatherCompletionsForObject(base);
            return;
          }

          const c = cm.getCursor();
          const prevToken = cm.getTokenAt({ line: c.line, ch: token.start - 0 });
          const prevPrevToken = cm.getTokenAt({ line: c.line, ch: token.start - 1 });
          if (prevToken.string == ":" && prevPrevToken.string == ":") {
            this.completeUtilsFunctions(token);
            return;
          }

          // If not, just look in the global object and any local scope
          // (reading into JS mode internals to get at the local and global variables)

          for (var v = token.state.localVars; v; v = v.next) {
            this.maybeAdd(forOrigin(v.name, 'local'));
          }
          for (var w = token.state.globalVars; w; w = w.next) {
            this.maybeAdd(forOrigin(w.name, 'global'));
          }
          if (!options || options.useGlobalScope !== false) {
            this.gatherCompletionsForObject(global);
          }

          javaScriptKeywords.map(keyword => forOrigin(keyword + ' ', 'keyword')).forEach(this.maybeAdd.bind(this));
        }

        /*MD ## Completions for an Object MD*/
        gatherCompletionsForObject(obj, hintPrefix = '') {
          this.allProps(obj, hintPrefix);

          if (typeof obj == "string") {
            this.forPropList(stringProps, hintPrefix + 'str');
          } else if (obj instanceof Array) {
            this.forPropList(arrayProps, hintPrefix + 'arr');
          } else if (obj instanceof Function) {
            this.forPropList(funcProps, hintPrefix + 'fun');
          }
        }

        forPropList(propList, origin) {
          propList.map(name => forOrigin(name, origin)).forEach(this.maybeAdd.bind(this));
        }

        allProps(obj, hintPrefix) {
          for (var o = obj; o; o = Object.getPrototypeOf(o)) {
            Object.getOwnPropertyNames(o).forEach(prop => this.addObjectProp(o, prop, hintPrefix));
          }
        }

        addObjectProp(obj, prop, hintPrefix) {
          let completion = prop;
          const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
          let value = descriptor.value;

          if (value) {
            if (typeof value === 'function') {
              let str = value.toString().split('\n').first.substring(0, 50);
              completion = {
                text: prop,
                innerRender(element, self, data) {
                  element.append(_element('span', _attributes(), _children(_childExpression(prop), _childText(' '), _childElement(_element('span', _attributes(_attributeStringLiteral('style', 'color: rgba(200, 200, 200, 0.9)')), _children(_childExpression(str)), {
                    file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                    end: {
                      column: 98,
                      line: 404
                    },
                    start: {
                      column: 40,
                      line: 404
                    }
                  }))), {
                    file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                    end: {
                      column: 105,
                      line: 404
                    },
                    start: {
                      column: 27,
                      line: 404
                    }
                  }));
                },
                hint: (cm, self, data) => {
                  const args = this.parseArgsFrom(`const fn = ${value}`, 'FunctionExpression') || this.parseArgsFrom(`class C {${value}}`, 'ClassMethod') || [];

                  this.completeFunction(cm, self, data, prop, args);
                  CodeMirror.commands.indentAuto(cm);
                }
              };
              this.maybeAdd(forOrigin(completion, hintPrefix + 'prop'));
              return;
            } else if (value.toString) {
              value = value.toString().substring(0, 50);
            }
            lively.n;
            completion = {
              text: prop,
              innerRender(element, self, data) {
                element.append(_element('span', _attributes(), _children(_childExpression(prop), _childText(' '), _childElement(_element('span', _attributes(_attributeStringLiteral('style', 'color: rgba(200, 200, 200, 0.9)')), _children(_childExpression(value)), {
                  file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                  end: {
                    column: 98,
                    line: 422
                  },
                  start: {
                    column: 38,
                    line: 422
                  }
                }))), {
                  file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                  end: {
                    column: 105,
                    line: 422
                  },
                  start: {
                    column: 25,
                    line: 422
                  }
                }));
              }
            };
          } else {}

          this.maybeAdd(forOrigin(completion, hintPrefix + 'prop'));
        }

        parseArgsFrom(code, nodeType) {
          const args = [];
          try {
            code.traverseAsAST({
              [nodeType](path) {
                const params = path.get('params');
                params.forEach(param => {
                  if (param.isIdentifier()) {
                    args.push(param.node.name);
                  } else {
                    // RestElement, Patterns
                    let arg = '';
                    param.traverse({
                      Identifier(path) {
                        arg += path.node.name;
                      }
                    });
                    args.push(arg);
                  }
                });
              }
            });
            return args;
          } catch (e) {}
        }

        /*MD ## Specific Completions MD*/
        completeMembersOfThis() {
          let currentClass;
          this.cm.eachLine(lineHandle => {
            const classMatch = lineHandle.text.match(/^(?:\s|export\s|default\s)*class\s+([_$a-zA-z][_$a-zA-Z0-9]*).*\{$/);
            if (classMatch) {
              currentClass = classMatch[1];
              // lively.notify(Object.getOwnPropertyNames(lineHandle));
              return;
            }

            const firstLine = lineHandle.text;
            const match = firstLine.match(/^\s*((?:async\s|get\s|set\s|static\s)*)([_$a-zA-z][_$a-zA-Z0-9]*)(\*?\s*\(.*\)\s*\{)$/);
            if (match) {
              const [, modifiers, name, argsAndBodyStart] = match;
              if (javaScriptKeywords.includes(name) || name === 'constructor') {
                return;
              }

              const myClass = currentClass;
              const completion = {
                text: name,
                innerRender(element, self, data) {
                  function grayish(content) {
                    const style = "color: rgba(200, 200, 200, 0.9); display:inline-block; max-width: 50ch;";
                    return _element('span', _attributes(_attributeExpression('style', style)), _children(_childExpression(content)), {
                      file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                      end: {
                        column: 57,
                        line: 481
                      },
                      start: {
                        column: 21,
                        line: 481
                      }
                    });
                  }
                  element.append(_element('span', _attributes(), _children(_childExpression(grayish(modifiers + (myClass ? myClass + '::' : ''))), _childExpression(name), _childExpression(grayish(argsAndBodyStart))), {
                    file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                    end: {
                      column: 127,
                      line: 483
                    },
                    start: {
                      column: 27,
                      line: 483
                    }
                  }));
                },
                hint: (cm, self, data) => {
                  if (modifiers.includes('get') || modifiers.includes('set')) {
                    cm.replaceRange(data.text, self.from, self.to);
                  } else {
                    const args = this.parseArgsFrom(`class C {${firstLine}}}`, 'ClassMethod') || [];
                    this.completeFunction(cm, self, data, name, args);
                  }
                  CodeMirror.commands.indentAuto(cm);
                }
              };
              // lively.notify(name);
              this.maybeAdd(forOrigin(completion, 'member'));
            }
          });
        }

        async completeFromImport(cm) {
          const getCompletionsFromImports = async () => {
            var _context2;

            // #TODO make faster with index and caching
            // but: #TODO what is the workflox / programming XP, we aim for?
            var text = await searchImports();
            var imports = (_context2 = text.split("\n").map(ea => ea.replace(/.*:/, "").replace(/;$/, "").replace(/"/g, "'")).filter(ea => !ea.match('\'\.\.?/')).filter(ea => ea.startsWith("import ")).sort(), uniq).call(_context2);
            imports.map(i => forOrigin(i, 'import')).forEach(this.maybeAdd.bind(this));
          };

          const getToken = cm.getTokenAt.bind(cm);

          // Find the token at the cursor
          var cursor = cm.getCursor();
          var token = getToken(cursor);

          // use a "line" token
          this.completions.token = {
            start: 0,
            end: cursor.ch,
            string: cm.getLine(cursor.line),
            state: token.state,
            type: null
          };

          await getCompletionsFromImports();
        }

        completeUtilsFunctions(token) {
          Object.keys(utils).filter(ea => ea.startsWith(token.string)).forEach(utilFuncName => {
            const completion = {
              text: utilFuncName,
              displayText: "util." + utilFuncName,
              hint: (cm, self, data) => {
                var offset = 0;
                cm.replaceRange(utilFuncName + "()", self.from, self.to);
                var lines = cm.getValue().split("\n");
                var imports = lines.filter(ea => ea.match(/^import .*from ['"]utils['"];?/));
                if (!imports.find(ea => ea.match(utilFuncName))) {
                  var utils = imports.find(ea => ea.match(/import {.*} from 'utils';/));
                  if (utils) {
                    var funcs = utils.replace(/.*{ */, "").replace(/ *}.*/, "").split(/ *, */);
                    funcs.push(utilFuncName);
                    lines = lines.map(ea => ea == utils ? `import { ${funcs.sort().join(", ")} } from 'utils';` : ea);
                    cm.setValue(lines.join("\n"));
                  } else {
                    var prefix = `import { ${utilFuncName} } from 'utils';\n`;
                    cm.setValue(prefix + cm.getValue());
                    offset += 1;
                  }
                }
                cm.setCursor({ line: self.from.line + offset, ch: self.from.ch + utilFuncName.length + "(".length });
              }
            };

            this.completions.maybeAdd(forOrigin(completion, 'utils'));
          });
        }

        completeLively() {
          const hintTexts = ['lively.', 'gs.'];
          hintTexts.forEach(text => {
            if (['variable', null].includes(this.completions.token.type)) {
              this.maybeAdd({
                text,
                hint: (cm, self, data) => {
                  cm.replaceRange(text, self.from, self.to);
                  CodeMirror.commands.indentAuto(cm);
                  CodeMirror.commands.autocomplete(cm);
                }
              });
            }
          });
        }

        completeDoItCommand() {
          const token = this.completions.token;
          const str = token.string;
          const cm = this.cm;
          var cursor = cm.getCursor();

          if (str.length >= 1 && 'DoItCommand'.startsWith(str)) {
            var tprop = token;
            const getToken = cm.getTokenAt.bind(cm);
            if (token.type == "variable") {
              const { type, string, start } = getToken(Pos(cursor.line, tprop.start));
              if (type === null && string === ' ') {
                const { type, string } = getToken(Pos(cursor.line, start));
                if (type === 'keyword' && string === 'new') {

                  this.maybeAdd({
                    text: 'DoItCommand(args)',
                    hint: async (cm, self, data) => {
                      cm.setSelection(self.from, self.to);
                      cm.replaceSelection(`DoItCommand({
  do: () => {
    
  },
  undo: () => {
    
  },
  redo: () => {
    
  }
})`, 'around');
                      indentSelections.call(cm);
                      const startLine = cm.getCursor('start').line;
                      cm.setCursor(startLine + 2, Infinity);
                      CodeMirror.commands.autocomplete(cm);
                    }
                  });
                }
              }
            }
          }
        }

        /*MD ## Tern Integration MD*/
        async completeFromTern() {
          const lcm = lcmFromCM(this.cm);
          const tw = await lcm.ternWrapper;
          const fileName = lcm.getTargetModule();
          const data = await tw.request({
            query: {
              type: "completions",
              types: true,
              file: fileName,
              end: this.cm.getCursor(),
              depths: true,
              docs: true,
              urls: true,
              origins: true,
              filter: false,
              caseInsensitive: false,
              guess: false,
              sort: false,
              expandWordForward: true,
              omitObjectPrototype: true,
              includeKeywords: false,
              inLiteral: true,
              start: undefined, // #TODO: improve by checking for selections first
              lineCharPositions: true
            },
            files: [{
              type: 'full',
              name: fileName,
              text: lcm.value
            }]
          });

          data.completions.map(({ name, type }) => forOrigin(type && type !== '?' ? {
            text: name,
            innerRender(element, self, data) {
              element.append(_element('span', _attributes(), _children(_childExpression(name), _childText(' '), _childElement(_element('span', _attributes(_attributeStringLiteral('style', 'color: rgba(200, 200, 200, 0.9); display:inline-block; max-width: 50ch;')), _children(_childExpression(type)), {
                file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                end: {
                  column: 135,
                  line: 653
                },
                start: {
                  column: 36,
                  line: 653
                }
              }))), {
                file: 'https://lively-kernel.org/lively4/lively4-portal/src/components/widgets/lively-code-mirror-hint.js',
                end: {
                  column: 142,
                  line: 653
                },
                start: {
                  column: 23,
                  line: 653
                }
              }));
            },
            hint: (cm, self, data) => {
              if (type.startsWith('fn(') && !/^[A-Z]/.test(name)) {
                const parseArgsFromTernType = str => {

                  const findMatchingParen = (str, start) => {
                    str = str.substring(start);

                    const matches = [...str.matchAll(/[()]/g)].map(match => ({ char: match[0], index: match.index }));

                    let openParens = 1;

                    for (let match of matches) {
                      const { char, index } = match;

                      if (char === '(') {
                        openParens++;
                      } else if (char === ')') {
                        openParens--;
                        if (openParens === 0) {
                          return index + start;
                        }
                      } else {
                        lively.error(`parseFnFromTern: match ${char} at position ${index} should never happen`);
                      }
                    }

                    return -1;
                  };

                  const start = str.indexOf('(') + 1;
                  const end = findMatchingParen(str, start);
                  const argsString = str.substring(start, end);
                  return argsString.split(', ').map(argWithType => argWithType.replace(/:.*/gm, '').replace(/\?/gm, ''));
                };

                this.completeFunction(cm, self, data, data.text, parseArgsFromTernType(type));
              } else {
                cm.replaceRange(data.text, self.from, self.to);
              }
              CodeMirror.commands.indentAuto(cm);
            }

          } : name, 'tern')).forEach(this.maybeAdd.bind(this));
        }

        /*MD ## Performing Completions MD*/
        completeFunction(cm, self, data, funcName, args = []) {
          cm.replaceRange(funcName + '(', self.from, self.to);
          cm.replaceSelection(')', 'start');

          const [firstArg, ...rest] = args;
          if (rest.length > 0) {
            cm.replaceSelection(', ' + rest.join(', '), 'start');
          }
          if (firstArg) {
            cm.replaceSelection(firstArg, 'around');
          }
        }

      }

      Object.defineProperty(_recorder_._src_components_widgets_lively_code_mirror_hint_js, 'CompletionsBuilder', {
        get() {
          return CompletionsBuilder;
        },

        set(thisIsVererySecretVariableName) {
          CompletionsBuilder = thisIsVererySecretVariableName;
          return true;
        },

        enumerable: true,
        configurable: true
      });
      function javascriptHint(cm, options) {
        return new CompletionsBuilder(cm).buildHint(options);
      }

      CodeMirror.registerHelper("hint", "javascript", javascriptHint);
    }
  };
});