const EditorWorker = 99;
const RendererWorker = 1;

const normalizeLine = line => {
  if (line.startsWith('Error: ')) {
    return line.slice('Error: '.length);
  }
  if (line.startsWith('VError: ')) {
    return line.slice('VError: '.length);
  }
  return line;
};
const getCombinedMessage = (error, message) => {
  const stringifiedError = normalizeLine(`${error}`);
  if (message) {
    return `${message}: ${stringifiedError}`;
  }
  return stringifiedError;
};
const NewLine$2 = '\n';
const getNewLineIndex$1 = (string, startIndex = undefined) => {
  return string.indexOf(NewLine$2, startIndex);
};
const mergeStacks = (parent, child) => {
  if (!child) {
    return parent;
  }
  const parentNewLineIndex = getNewLineIndex$1(parent);
  const childNewLineIndex = getNewLineIndex$1(child);
  if (childNewLineIndex === -1) {
    return parent;
  }
  const parentFirstLine = parent.slice(0, parentNewLineIndex);
  const childRest = child.slice(childNewLineIndex);
  const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
  if (parentFirstLine.includes(childFirstLine)) {
    return parentFirstLine + childRest;
  }
  return child;
};
class VError extends Error {
  constructor(error, message) {
    const combinedMessage = getCombinedMessage(error, message);
    super(combinedMessage);
    this.name = 'VError';
    if (error instanceof Error) {
      this.stack = mergeStacks(this.stack, error.stack);
    }
    if (error.codeFrame) {
      // @ts-ignore
      this.codeFrame = error.codeFrame;
    }
    if (error.code) {
      // @ts-ignore
      this.code = error.code;
    }
  }
}

class AssertionError extends Error {
  constructor(message) {
    super(message);
    this.name = 'AssertionError';
  }
}
const Object$1 = 1;
const Number$1 = 2;
const Array$1 = 3;
const String$1 = 4;
const Boolean$1 = 5;
const Function = 6;
const Null = 7;
const Unknown = 8;
const getType = value => {
  switch (typeof value) {
    case 'number':
      return Number$1;
    case 'function':
      return Function;
    case 'string':
      return String$1;
    case 'object':
      if (value === null) {
        return Null;
      }
      if (Array.isArray(value)) {
        return Array$1;
      }
      return Object$1;
    case 'boolean':
      return Boolean$1;
    default:
      return Unknown;
  }
};
const object = value => {
  const type = getType(value);
  if (type !== Object$1) {
    throw new AssertionError('expected value to be of type object');
  }
};
const number = value => {
  const type = getType(value);
  if (type !== Number$1) {
    throw new AssertionError('expected value to be of type number');
  }
};
const array = value => {
  const type = getType(value);
  if (type !== Array$1) {
    throw new AssertionError('expected value to be of type array');
  }
};
const string = value => {
  const type = getType(value);
  if (type !== String$1) {
    throw new AssertionError('expected value to be of type string');
  }
};

class CommandNotFoundError extends Error {
  constructor(command) {
    super(`Command not found ${command}`);
    this.name = 'CommandNotFoundError';
  }
}
const commands = Object.create(null);
const register$1 = commandMap => {
  Object.assign(commands, commandMap);
};
const getCommand = key => {
  return commands[key];
};
const execute = (command, ...args) => {
  const fn = getCommand(command);
  if (!fn) {
    throw new CommandNotFoundError(command);
  }
  return fn(...args);
};

const createMockRpc = ({
  commandMap
}) => {
  const invocations = [];
  const invoke = (method, ...params) => {
    invocations.push([method, ...params]);
    const command = commandMap[method];
    if (!command) {
      throw new Error(`command ${method} not found`);
    }
    return command(...params);
  };
  const mockRpc = {
    invocations,
    invoke,
    invokeAndTransfer: invoke
  };
  return mockRpc;
};

const rpcs = Object.create(null);
const set$3 = (id, rpc) => {
  rpcs[id] = rpc;
};
const get$2 = id => {
  return rpcs[id];
};
const remove$1 = id => {
  delete rpcs[id];
};

/* eslint-disable  @typescript-eslint/explicit-function-return-type */
const create$b = rpcId => {
  return {
    async dispose() {
      const rpc = get$2(rpcId);
      await rpc.dispose();
    },
    // @ts-ignore
    invoke(method, ...params) {
      const rpc = get$2(rpcId);
      // @ts-ignore
      return rpc.invoke(method, ...params);
    },
    // @ts-ignore
    invokeAndTransfer(method, ...params) {
      const rpc = get$2(rpcId);
      // @ts-ignore
      return rpc.invokeAndTransfer(method, ...params);
    },
    registerMockRpc(commandMap) {
      const mockRpc = createMockRpc({
        commandMap
      });
      set$3(rpcId, mockRpc);
      // @ts-ignore
      mockRpc[Symbol.dispose] = () => {
        remove$1(rpcId);
      };
      // @ts-ignore
      return mockRpc;
    },
    set(rpc) {
      set$3(rpcId, rpc);
    }
  };
};

const {
  invoke: invoke$2,
  set: set$2
} = create$b(EditorWorker);
const getLines = async editorUid => {
  const lines = await invoke$2('Editor.getLines2', editorUid);
  return lines;
};

const {
  invoke: invoke$1,
  invokeAndTransfer,
  set: set$1
} = create$b(RendererWorker);
const sendMessagePortToEditorWorker = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToEditorWorker', port, command, rpcId);
};
const setFocus = key => {
  return invoke$1('Focus.setFocus', key);
};
const getFileIcon = async options => {
  return invoke$1('IconTheme.getFileIcon', options);
};
const getFolderIcon = async options => {
  return invoke$1('IconTheme.getFolderIcon', options);
};
const closeWidget$1 = async widgetId => {
  return invoke$1('Viewlet.closeWidget', widgetId);
};
const getActiveEditorId = () => {
  return invoke$1('GetActiveEditor.getActiveEditorId');
};
const openUri$1 = async (uri, focus, options) => {
  await invoke$1('Main.openUri', uri, focus, options);
};
const showErrorDialog$1 = async errorInfo => {
  await invoke$1('ErrorHandling.showErrorDialog', errorInfo);
};

const closeWidget = async id => {
  // @ts-ignore
  await closeWidget$1(id);
};

const close = async state => {
  await closeWidget(state.uid);
  return state;
};

const User = 1;
const Script = 2;

const minimumSliderSize = 20;

const Default$1 = 0;
const Finished = 2;

const toCommandId = key => {
  const dotIndex = key.indexOf('.');
  return key.slice(dotIndex + 1);
};
const create$a = () => {
  const states = Object.create(null);
  const commandMapRef = {};
  return {
    get(uid) {
      return states[uid];
    },
    set(uid, oldState, newState) {
      states[uid] = {
        oldState,
        newState
      };
    },
    dispose(uid) {
      delete states[uid];
    },
    getKeys() {
      return Object.keys(states).map(key => {
        return Number.parseInt(key);
      });
    },
    clear() {
      for (const key of Object.keys(states)) {
        delete states[key];
      }
    },
    wrapCommand(fn) {
      const wrapped = async (uid, ...args) => {
        const {
          oldState,
          newState
        } = states[uid];
        const newerState = await fn(newState, ...args);
        if (oldState === newerState || newState === newerState) {
          return;
        }
        const latest = states[uid];
        states[uid] = {
          oldState: latest.oldState,
          newState: newerState
        };
      };
      return wrapped;
    },
    wrapGetter(fn) {
      const wrapped = (uid, ...args) => {
        const {
          newState
        } = states[uid];
        return fn(newState, ...args);
      };
      return wrapped;
    },
    diff(uid, modules, numbers) {
      const {
        oldState,
        newState
      } = states[uid];
      const diffResult = [];
      for (let i = 0; i < modules.length; i++) {
        const fn = modules[i];
        if (!fn(oldState, newState)) {
          diffResult.push(numbers[i]);
        }
      }
      return diffResult;
    },
    getCommandIds() {
      const keys = Object.keys(commandMapRef);
      const ids = keys.map(toCommandId);
      return ids;
    },
    registerCommands(commandMap) {
      Object.assign(commandMapRef, commandMap);
    }
  };
};

const {
  dispose: dispose$1,
  get: get$1,
  getCommandIds,
  registerCommands,
  set,
  wrapCommand
} = create$a();

const create$9 = ({
  headerHeight = 0,
  itemHeight,
  minimumSliderSize = 20
}) => {
  return {
    deltaY: 0,
    finalDeltaY: 0,
    focusedIndex: -1,
    headerHeight,
    itemHeight,
    items: [],
    maxLineY: 0,
    minimumSliderSize,
    minLineY: 0,
    scrollBarActive: false,
    scrollBarHeight: 0,
    touchDifference: 0,
    touchOffsetY: 0,
    touchTimeStamp: 0
  };
};
const getListHeight$1 = (height, headerHeight) => {
  if (headerHeight) {
    return height - headerHeight;
  }
  return headerHeight;
};
const setDeltaY = (state, deltaY) => {
  object(state);
  number(deltaY);
  const {
    headerHeight,
    height,
    itemHeight,
    items
  } = state;
  const listHeight = getListHeight$1(height, headerHeight);
  const itemsLength = items.length;
  const finalDeltaY = itemsLength * itemHeight - listHeight;
  if (deltaY < 0) {
    deltaY = 0;
  } else if (deltaY > finalDeltaY) {
    deltaY = Math.max(finalDeltaY, 0);
  }
  if (state.deltaY === deltaY) {
    return state;
  }
  const minLineY = Math.round(deltaY / itemHeight);
  const maxLineY = minLineY + Math.round(listHeight / itemHeight);
  number(minLineY);
  number(maxLineY);
  return {
    ...state,
    deltaY,
    maxLineY,
    minLineY
  };
};
const handleWheel = (state, deltaMode, deltaY) => {
  object(state);
  number(deltaMode);
  number(deltaY);
  return setDeltaY(state, state.deltaY + deltaY);
};

const create$8 = (uid, uri, listItemHeight, x, y, width, height, platform, args, workspaceUri, assetDir) => {
  const state = {
    allowEmptyResult: false,
    cursorOffset: 0,
    height: 300,
    icons: [],
    maxVisibleItems: 12,
    picks: [],
    recentPickIds: Object.create(null),
    recentPicks: [],
    state: Default$1,
    top: 50,
    uid,
    uri,
    versionId: 0,
    warned: [],
    width: 600,
    workspaceUri,
    ...create$9({
      headerHeight: 38,
      itemHeight: listItemHeight,
      minimumSliderSize: minimumSliderSize
    }),
    args,
    assetDir,
    fileIconCache: Object.create(null),
    focused: false,
    inputSource: User,
    placeholder: '',
    platform,
    value: ''
  };
  set(uid, state, state);
};

const RenderItems = 1;
const RenderFocus = 2;
const RenderValue = 3;
const RenderCursorOffset = 7;
const RenderFocusedIndex = 8;
const Height = 9;

const diffType$4 = RenderFocus;
const isEqual$4 = (oldState, newState) => {
  return oldState.focused === newState.focused;
};

const diffType$3 = RenderFocusedIndex;
const isEqual$3 = (oldState, newState) => {
  return oldState.focusedIndex === newState.focusedIndex;
};

const diffType$2 = Height;
const isEqual$2 = (oldState, newState) => {
  return oldState.items.length === newState.items.length;
};

const diffType$1 = RenderItems;
const isEqual$1 = (oldState, newState) => {
  return oldState.items === newState.items && oldState.minLineY === newState.minLineY && oldState.maxLineY === newState.maxLineY && oldState.focusedIndex === newState.focusedIndex;
};

const diffType = RenderValue;
const isEqual = (oldState, newState) => {
  return newState.inputSource === User || oldState.value === newState.value;
};

const modules = [isEqual$2, isEqual$1, isEqual, isEqual$3, isEqual$4];
const numbers = [diffType$2, diffType$1, diffType, diffType$3, diffType$4];

const diff = (oldState, newState) => {
  const diffResult = [];
  for (let i = 0; i < modules.length; i++) {
    const fn = modules[i];
    if (!fn(oldState, newState)) {
      diffResult.push(numbers[i]);
    }
  }
  return diffResult;
};

const diff2 = uid => {
  const {
    newState,
    oldState
  } = get$1(uid);
  return diff(oldState, newState);
};

const dispose = uid => {
  dispose$1(uid);
};

const setColorTheme = id => {
  return invoke$1(/* ColorTheme.setColorTheme */'ColorTheme.setColorTheme', /* colorThemeId */id);
};

const focusPick$1 = async pick => {
  const {
    label
  } = pick;
  await setColorTheme(label);
};

const ColorTheme$1 = 0;
const Commands$1 = 1;
const Custom$2 = 2;
const File$2 = 3;
const GoToColumn$1 = 4;
const GoToLine$2 = 5;
const Help$2 = 6;
const Recent$1 = 7;
const Symbol$3 = 8;
const View$3 = 9;
const WorkspaceSymbol$2 = 10;
const EveryThing$1 = 100;

const noop$1 = async () => {};
const getFn$2 = id => {
  switch (id) {
    case ColorTheme$1:
      return focusPick$1;
    default:
      return noop$1;
  }
};
const focusPick = (id, pick) => {
  const fn = getFn$2(id);
  return fn(pick);
};

const getIconsCached = (paths, fileIconCache) => {
  return paths.map(path => fileIconCache[path]);
};

const getMissingIconRequests = (dirents, fileIconCache) => {
  const missingRequests = [];
  for (const dirent of dirents) {
    if (!dirent.path) {
      continue;
    }
    if (!(dirent.path in fileIconCache)) {
      missingRequests.push({
        name: dirent.name,
        path: dirent.path,
        type: dirent.type
      });
    }
  }
  return missingRequests;
};

const None$2 = 0;
const Directory = 3;
const File$1 = 7;

const requestFileIcon = async request => {
  if (!request.name) {
    return '';
  }
  return request.type === File$1 ? getFileIcon({
    name: request.name
  }) : getFolderIcon({
    name: request.name
  });
};
const requestFileIcons = async requests => {
  const promises = requests.map(requestFileIcon);
  return Promise.all(promises);
};

const updateIconCache = (iconCache, missingRequests, newIcons) => {
  if (missingRequests.length === 0) {
    return iconCache;
  }
  const newFileIconCache = {
    ...iconCache
  };
  for (let i = 0; i < missingRequests.length; i++) {
    const request = missingRequests[i];
    const icon = newIcons[i];
    newFileIconCache[request.path] = icon;
  }
  return newFileIconCache;
};

const getPath = dirent => {
  return dirent.path;
};
const toDirent = pick => {
  const dirent = {
    name: pick.label,
    path: pick.uri,
    type: pick.direntType
  };
  return dirent;
};
const getQuickPickFileIcons = async (items, fileIconCache) => {
  const dirents = items.map(toDirent);
  const missingRequests = getMissingIconRequests(dirents, fileIconCache);
  const newIcons = await requestFileIcons(missingRequests);
  const newFileIconCache = updateIconCache(fileIconCache, missingRequests, newIcons);
  const paths = dirents.map(getPath);
  const icons = getIconsCached(paths, newFileIconCache);
  return {
    icons,
    newFileIconCache
  };
};

const focusIndex = async (state, index) => {
  const {
    fileIconCache,
    items,
    maxLineY,
    maxVisibleItems,
    minLineY,
    providerId
  } = state;
  await focusPick(providerId, items[index]);
  if (index < minLineY + 1) {
    const minLineY = index;
    const maxLineY = Math.min(index + maxVisibleItems, items.length - 1);
    const sliced = items.slice(minLineY, maxLineY);
    const {
      icons,
      newFileIconCache
    } = await getQuickPickFileIcons(sliced, fileIconCache);

    // TODO need to scroll up
    return {
      ...state,
      fileIconCache: newFileIconCache,
      focusedIndex: index,
      icons,
      maxLineY,
      minLineY
    };
  }
  if (index >= maxLineY - 1) {
    // TODO need to scroll down
    const maxLineY = index + 1;
    const minLineY = Math.max(maxLineY - maxVisibleItems, 0);
    const sliced = items.slice(minLineY, maxLineY);
    const {
      icons,
      newFileIconCache
    } = await getQuickPickFileIcons(sliced, fileIconCache);
    return {
      ...state,
      fileIconCache: newFileIconCache,
      focusedIndex: index,
      icons,
      maxLineY,
      minLineY
    };
  }
  const sliced = items.slice(minLineY, maxLineY);
  const {
    icons,
    newFileIconCache
  } = await getQuickPickFileIcons(sliced, fileIconCache);
  return {
    ...state,
    fileIconCache: newFileIconCache,
    focusedIndex: index,
    icons
  };
};

const first = () => {
  return 0;
};
const last = items => {
  return items.length - 1;
};
const next = (items, index) => {
  return (index + 1) % items.length;
};
const previous = (items, index) => {
  return index === 0 ? items.length - 1 : index - 1;
};

const focusFirst = state => {
  return focusIndex(state, first());
};

const focusLast = state => {
  const {
    items
  } = state;
  return focusIndex(state, last(items));
};

const focusNext = state => {
  const {
    focusedIndex,
    items
  } = state;
  const nextIndex = next(items, focusedIndex);
  return focusIndex(state, nextIndex);
};

const focusPrevious = state => {
  const {
    focusedIndex,
    items
  } = state;
  const previousIndex = previous(items, focusedIndex);
  return focusIndex(state, previousIndex);
};

const Div = 4;
const Input = 6;
const Span = 8;
const Text = 12;
const Img = 17;

const SetDom2 = 'Viewlet.setDom2';

const FocusQuickPickInput = 20;

const Enter = 3;
const Escape = 8;
const PageUp = 10;
const PageDown = 11;
const UpArrow = 14;
const DownArrow = 16;

const getKeyBindings = () => {
  return [{
    args: ['QuickPick'],
    command: 'Viewlet.closeWidget',
    key: Escape,
    when: FocusQuickPickInput
  }, {
    command: 'QuickPick.focusPrevious',
    key: UpArrow,
    when: FocusQuickPickInput
  }, {
    command: 'QuickPick.focusNext',
    key: DownArrow,
    when: FocusQuickPickInput
  }, {
    command: 'QuickPick.focusFirst',
    key: PageUp,
    when: FocusQuickPickInput
  }, {
    command: 'QuickPick.focusLast',
    key: PageDown,
    when: FocusQuickPickInput
  }, {
    command: 'QuickPick.selectCurrentIndex',
    key: Enter,
    when: FocusQuickPickInput
  }];
};

const getNewValueDeleteContentBackward = (value, selectionStart, selectionEnd, data) => {
  const after = value.slice(selectionEnd);
  if (selectionStart === selectionEnd) {
    const before = value.slice(0, selectionStart - 1);
    const newValue = before + after;
    return {
      cursorOffset: before.length,
      newValue
    };
  }
  const before = value.slice(0, selectionStart);
  const newValue = before + after;
  return {
    cursorOffset: selectionStart,
    newValue
  };
};

const getNewValueDeleteContentForward = (value, selectionStart, selectionEnd, data) => {
  const before = value.slice(0, selectionStart);
  if (selectionStart === selectionEnd) {
    const after = value.slice(selectionEnd + 1);
    const newValue = before + after;
    return {
      cursorOffset: selectionStart,
      newValue
    };
  }
  const after = value.slice(selectionEnd);
  const newValue = before + after;
  return {
    cursorOffset: selectionStart,
    newValue
  };
};

const RE_ALPHA_NUMERIC = /[a-z\d]/i;
const isAlphaNumeric = character => {
  return RE_ALPHA_NUMERIC.test(character);
};

const getNewValueDeleteWordBackward = (value, selectionStart, selectionEnd, data) => {
  const after = value.slice(selectionEnd);
  if (selectionStart === selectionEnd) {
    let startIndex = Math.max(selectionStart - 1, 0);
    while (startIndex > 0 && isAlphaNumeric(value[startIndex])) {
      startIndex--;
    }
    const before = value.slice(0, startIndex);
    const newValue = before + after;
    return {
      cursorOffset: before.length,
      newValue
    };
  }
  const before = value.slice(0, selectionStart);
  const newValue = before + after;
  return {
    cursorOffset: selectionStart,
    newValue
  };
};

const getNewValueDeleteWordForward = (value, selectionStart, selectionEnd, data) => {
  const before = value.slice(0, selectionStart);
  if (selectionStart === selectionEnd) {
    let startIndex = Math.min(selectionStart + 1, value.length - 1);
    while (startIndex < value.length && isAlphaNumeric(value[startIndex])) {
      startIndex++;
    }
    const after = value.slice(startIndex);
    const newValue = before + after;
    return {
      cursorOffset: before.length,
      newValue
    };
  }
  const after = value.slice(selectionEnd);
  const newValue = before + after;
  return {
    cursorOffset: selectionStart,
    newValue
  };
};

const getNewValueInsertText = (value, selectionStart, selectionEnd, data) => {
  if (selectionStart === value.length) {
    const newValue = value + data;
    return {
      cursorOffset: newValue.length,
      newValue
    };
  }
  const before = value.slice(0, selectionStart);
  const after = value.slice(selectionEnd);
  const newValue = before + data + after;
  return {
    cursorOffset: selectionStart + data.length,
    newValue
  };
};

const getNewValueInsertCompositionText = (value, selectionStart, selectionEnd, data) => {
  return getNewValueInsertText(value, selectionStart, selectionEnd, data);
};

const getNewValueInsertLineBreak = (value, selectionStart, selectionEnd, data) => {
  return {
    cursorOffset: selectionEnd,
    newValue: value
  };
};

const InsertText = 'insertText';
const DeleteContentBackward = 'deleteContentBackward';
const DeleteContentForward = 'deleteContentForward';
const DeleteWordForward = 'deleteWordForward';
const DeleteWordBackward = 'deleteWordBackward';
const InsertLineBreak = 'insertLineBreak';
const InsertCompositionText = 'insertCompositionText';
const InsertFromPaste = 'insertFromPaste';

const getNewValueFunction = inputType => {
  switch (inputType) {
    case DeleteContentBackward:
      return getNewValueDeleteContentBackward;
    case DeleteContentForward:
      return getNewValueDeleteContentForward;
    case DeleteWordBackward:
      return getNewValueDeleteWordBackward;
    case DeleteWordForward:
      return getNewValueDeleteWordForward;
    case InsertCompositionText:
      return getNewValueInsertCompositionText;
    case InsertFromPaste:
    case InsertText:
      return getNewValueInsertText;
    case InsertLineBreak:
      return getNewValueInsertLineBreak;
    default:
      throw new Error(`unsupported input type ${inputType}`);
  }
};

const getNewValue = (value, inputType, data, selectionStart, selectionEnd) => {
  const fn = getNewValueFunction(inputType);
  return fn(value, selectionStart, selectionEnd, data);
};

const Diagonal = 1;
const Left = 2;

// based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)

const createTable = size => {
  const table = [];
  for (let i = 0; i < size; i++) {
    const row = new Uint8Array(size);
    table.push(row);
  }
  return table;
};
const EmptyMatches = [];
const Dash = '-';
const Dot = '.';
const EmptyString = '';
const Space = ' ';
const Underline = '_';
const T = 't';
const isLowerCase = char => {
  return char === char.toLowerCase();
};
const isUpperCase = char => {
  return char === char.toUpperCase();
};

// based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
const isGap = (columnCharBefore, columnChar) => {
  switch (columnCharBefore) {
    case Dash:
    case Underline:
    case EmptyString:
    case T:
    case Space:
    case Dot:
      return true;
  }
  if (isLowerCase(columnCharBefore) && isUpperCase(columnChar)) {
    return true;
  }
  return false;
};

// based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
const getScore = (rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, isDiagonalMatch) => {
  if (rowCharLow !== columnCharLow) {
    return -1;
  }
  const isMatch = rowChar === columnChar;
  if (isMatch) {
    if (isDiagonalMatch) {
      return 8;
    }
    if (isGap(columnCharBefore, columnChar)) {
      return 8;
    }
    return 5;
  }
  if (isGap(columnCharBefore, columnChar)) {
    return 8;
  }
  return 5;
};

// based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)

const isPatternInWord = (patternLow, patternPos, patternLen, wordLow, wordPos, wordLen) => {
  while (patternPos < patternLen && wordPos < wordLen) {
    if (patternLow[patternPos] === wordLow[wordPos]) {
      patternPos += 1;
    }
    wordPos += 1;
  }
  return patternPos === patternLen; // pattern must be exhausted
};

// based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
const traceHighlights = (table, arrows, patternLength, wordLength) => {
  let row = patternLength;
  let column = wordLength;
  const matches = [];
  while (row >= 1 && column >= 1) {
    const arrow = arrows[row][column];
    if (arrow === Left) {
      column--;
    } else if (arrow === Diagonal) {
      row--;
      column--;
      const start = column + 1;
      while (row >= 1 && column >= 1) {
        const arrow = arrows[row][column];
        if (arrow === Left) {
          break;
        }
        if (arrow === Diagonal) {
          row--;
          column--;
        }
      }
      const end = column;
      matches.unshift(end, start);
    }
  }
  matches.unshift(table[patternLength][wordLength - 1]);
  return matches;
};

// based on https://github.com/microsoft/vscode/blob/3059063b805ed0ac10a6d9539e213386bfcfb852/src/vs/base/common/filters.ts by Microsoft (License MIT)
const gridSize = 128;
const table = createTable(gridSize);
const arrows = createTable(gridSize);
const fuzzySearch = (pattern, word) => {
  const patternLength = Math.min(pattern.length, gridSize - 1);
  const wordLength = Math.min(word.length, gridSize - 1);
  const patternLower = pattern.toLowerCase();
  const wordLower = word.toLowerCase();
  if (!isPatternInWord(patternLower, 0, patternLength, wordLower, 0, wordLength)) {
    return EmptyMatches;
  }
  let strongMatch = false;
  for (let row = 1; row < patternLength + 1; row++) {
    const rowChar = pattern[row - 1];
    const rowCharLow = patternLower[row - 1];
    for (let column = 1; column < wordLength + 1; column++) {
      const columnChar = word[column - 1];
      const columnCharLow = wordLower[column - 1];
      const columnCharBefore = word[column - 2] || '';
      const isDiagonalMatch = arrows[row - 1][column - 1] === Diagonal;
      const score = getScore(rowCharLow, rowChar, columnCharBefore, columnCharLow, columnChar, isDiagonalMatch);
      if (row === 1 && score > 5) {
        strongMatch = true;
      }
      let diagonalScore = score + table[row - 1][column - 1];
      if (isDiagonalMatch && score !== -1) {
        diagonalScore += 2;
      }
      const leftScore = table[row][column - 1];
      if (leftScore > diagonalScore) {
        table[row][column] = leftScore;
        arrows[row][column] = Left;
      } else {
        table[row][column] = diagonalScore;
        arrows[row][column] = Diagonal;
      }
    }
  }
  if (!strongMatch) {
    return EmptyMatches;
  }
  const highlights = traceHighlights(table, arrows, patternLength, wordLength);
  return highlights;
};

const filterQuickPickItem = (pattern, word) => {
  const matches = fuzzySearch(pattern, word);
  return matches;
};

const filterQuickPickItems = (items, value) => {
  if (!value) {
    return items;
  }
  const results = [];
  for (const item of items) {
    const filterValue = item.label;
    const matches = filterQuickPickItem(value, filterValue);
    if (matches.length > 0) {
      results.push({
        ...item,
        matches
      });
    }
  }
  return results;
};

const Command = '>';
const Symbol$2 = '@';
const WorkspaceSymbol$1 = '#';
const GoToLine$1 = ':';
const View$2 = 'view ';
const None$1 = '';
const Help$1 = '?';
const GoToColumn = '::';

const getQuickPickPrefix = value => {
  if (value.startsWith(Command)) {
    return Command;
  }
  if (value.startsWith(Symbol$2)) {
    return Symbol$2;
  }
  if (value.startsWith(WorkspaceSymbol$1)) {
    return WorkspaceSymbol$1;
  }
  if (value.startsWith(GoToColumn)) {
    return GoToColumn;
  }
  if (value.startsWith(GoToLine$1)) {
    return GoToLine$1;
  }
  if (value.startsWith(View$2)) {
    return View$2;
  }
  return None$1;
};

const noop = value => {
  return value;
};
const getFilterValueEverything = value => {
  const prefix = getQuickPickPrefix(value);
  const prefixLength = prefix.length;
  return value.slice(prefixLength).trim();
};
const getValueGoToColumn = value => {
  return '';
};
const getValueGoToLine = value => {
  return '';
};
const getFn$1 = id => {
  switch (id) {
    case EveryThing$1:
      return getFilterValueEverything;
    case GoToColumn$1:
      return getValueGoToColumn;
    case GoToLine$2:
      return getValueGoToLine;
    default:
      return noop;
  }
};
const getFilterValue = (id, subId, value) => {
  if (subId === GoToColumn$1) {
    return getValueGoToColumn();
  }
  if (subId === GoToLine$2) {
    return getValueGoToLine();
  }
  const fn = getFn$1(id);
  const filterValue = fn(value);
  return filterValue;
};

const getFinalDeltaY = (height, itemHeight, itemsLength) => {
  const contentHeight = itemsLength * itemHeight;
  const finalDeltaY = Math.max(contentHeight - height, 0);
  return finalDeltaY;
};

const getListHeight = (itemsLength, itemHeight, maxHeight) => {
  number(itemsLength);
  number(itemHeight);
  number(maxHeight);
  if (itemsLength === 0) {
    return itemHeight;
  }
  const totalHeight = itemsLength * itemHeight;
  return Math.min(totalHeight, maxHeight);
};

const getColorThemeNames = async (assetDir, platform) => {
  return invoke$1('ColorTheme.getColorThemeNames', assetDir, platform);
};

const toProtoVisibleItem$3 = name => {
  const pick = {
    description: '',
    direntType: 0,
    fileIcon: '',
    icon: '',
    label: name,
    matches: [],
    uri: ''
  };
  return pick;
};
const getPicks$c = async (searchValue, args, {
  assetDir = '',
  platform = 0
} = {}) => {
  const colorThemeNames = await getColorThemeNames(assetDir, platform);
  const picks = colorThemeNames.map(toProtoVisibleItem$3);
  return picks;
};

const handleError = async (error, notify = true, prefix = '') => {
  console.error(error);
};
const showErrorDialog = async error => {
  const {
    code
  } = error;
  const {
    message
  } = error;
  const {
    stack
  } = error;
  const {
    name
  } = error;
  const errorInfo = {
    code,
    message,
    name,
    stack
  };
  await showErrorDialog$1(errorInfo);
};
const warn$1 = (...args) => {
  console.warn(...args);
};

const state$2 = {
  menuEntries: []
};
const getAll = async () => {
  try {
    // @ts-ignore
    const entries = await invoke$1('Layout.getAllQuickPickMenuEntries');
    return entries || [];
  } catch {
    // ignore
  }
  return state$2.menuEntries;
};
const add = menuEntries => {
  state$2.menuEntries = [...state$2.menuEntries, ...menuEntries];
};

// TODO combine Ajax with cache (specify strategy: cacheFirst, networkFirst)
const getBuiltinPicks = async () => {
  const builtinPicks = await getAll();
  return builtinPicks;
};
const prefixIdWithExt = item => {
  if (!item.label) {
    warn$1('[QuickPick] item has missing label', item);
  }
  if (!item.id) {
    warn$1('[QuickPick] item has missing id', item);
  }
  return {
    ...item,
    id: `ext.${item.id}`,
    label: item.label || item.id
  };
};
const getExtensionPicks = async (assetDir, platform) => {
  try {
    // TODO
    // Assert.string(assetDir)
    // Assert.number(platform)
    // TODO ask extension management worker directly
    // TODO don't call this every time, cache the results
    const extensionPicks = await invoke$1('ExtensionHost.getCommands', assetDir, platform);
    if (!extensionPicks) {
      return [];
    }
    const mappedPicks = extensionPicks.map(prefixIdWithExt);
    return mappedPicks;
  } catch (error) {
    console.error(`Failed to get extension picks: ${error}`);
    return [];
  }
};
const toProtoVisibleItem$2 = item => {
  const pick = {
    // @ts-ignore
    args: item.args,
    description: '',
    direntType: 0,
    fileIcon: '',
    icon: '',
    // @ts-ignore
    id: item.id,
    label: item.label,
    matches: [],
    uri: ''
  };
  // @ts-ignore
  return pick;
};
const getPicks$b = async (value, args, {
  assetDir = '',
  platform = 0
} = {}) => {
  // TODO get picks in parallel
  const builtinPicks = await getBuiltinPicks();
  const extensionPicks = await getExtensionPicks(assetDir, platform);
  const allPicks = [...builtinPicks, ...extensionPicks];
  const converted = allPicks.map(toProtoVisibleItem$2);
  return converted;
};

const toProtoVisibleItem$1 = item => {
  const {
    label
  } = item;
  return {
    description: '',
    direntType: 0,
    fileIcon: '',
    icon: '',
    label,
    matches: [],
    uri: ''
  };
};
const getPicks$a = async (searchValue, args) => {
  const items = args[1] || [];
  const mapped = items.map(toProtoVisibleItem$1);
  return mapped;
};

const emptyMatches = [];

const getWorkspacePath = async () => {
  return invoke$1('Workspace.getPath');
};

const RE_PROTOCOL = /^([a-z-]+):\/\//;
const getProtocol = uri => {
  const protocolMatch = uri.match(RE_PROTOCOL);
  if (protocolMatch) {
    return protocolMatch[1];
  }
  return '';
};

const state$1 = Object.create(null);
const register = modules => {
  Object.assign(state$1, modules);
};
const getFn = protocol => {
  return state$1[protocol];
};

const searchFile$5 = async (path, value, prepare, assetDir) => {
  const protocol = getProtocol(path);
  // TODO call different providers depending on protocol
  const fn = getFn(protocol);
  if (!fn) {
    throw new Error(`No search handler registered for protocol: ${protocol}`);
  }
  const result = await fn(path, value, prepare, assetDir);
  return result;
};

// TODO this should be in FileSystem module
const pathBaseName = path => {
  return path.slice(path.lastIndexOf('/') + 1);
};

// TODO this should be in FileSystem module
const pathDirName = path => {
  const pathSeparator = '/';
  const index = path.lastIndexOf(pathSeparator);
  if (index === -1) {
    return '';
  }
  return path.slice(0, index);
};

const searchFile$4 = async (path, value) => {
  const prepare = true;
  const files = await searchFile$5(/* path */path, /* searchTerm */value, prepare, '');
  return files;
};
const convertToPick = uri => {
  const baseName = pathBaseName(uri);
  const dirName = pathDirName(uri);
  return {
    description: dirName,
    direntType: File$1,
    fileIcon: '',
    icon: '',
    label: baseName,
    matches: emptyMatches,
    uri
  };
};

// TODO handle files differently
// e.g. when there are many files, don't need
// to compute the fileIcon for all files

const getPicks$9 = async searchValue => {
  // TODO cache workspace path
  const workspace = await getWorkspacePath();
  if (!workspace) {
    return [];
  }
  const files = await searchFile$4(workspace, searchValue);
  const picks = files.map(convertToPick);
  return picks;
};

const getText = async () => {
  // TODO
  const id = await getActiveEditorId();
  const lines = await getLines(id);
  return lines.join('\n');
};

const getPicksGoToColumnBase = async () => {
  const text = await getText();
  return [{
    description: '',
    direntType: 0,
    fileIcon: '',
    icon: '',
    label: `Type a character position to go to (from 1 to ${text.length})`,
    matches: [],
    uri: ''
  }];
};

const getPosition = (text, wantedColumn) => {
  let row = 0;
  let column = 0;
  for (let i = 0; i < wantedColumn; i++) {
    if (text[i] === '\n') {
      row++;
      column = 0;
    } else {
      column++;
    }
  }
  return {
    column,
    row
  };
};

const emptyObject = {};
const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
const i18nString = (key, placeholders = emptyObject) => {
  if (placeholders === emptyObject) {
    return key;
  }
  const replacer = (match, rest) => {
    return placeholders[rest];
  };
  return key.replaceAll(RE_PLACEHOLDER, replacer);
};

const GoToFile = 'Go to file';
const GoToLineColumn = 'Go to Line / Column';
const GoToSymbolInEditor = 'Go to Symbol in Editor';
const NoResults = 'No Results';
const OpenView = 'Open View';
const QuickOpen = 'Quick open';
const SearchForText = 'Search for text';
const ShowAndRunCommands = 'Show And Run Commands';
const TypeNameOfCommandToRun = 'Type the name of a command to run.';
const PressEnterToGoToLine = `Press 'Enter' to go to line {PH1} column {PH2}`;

const pressEnterToGoToLine = (row, column) => {
  return i18nString(PressEnterToGoToLine, {
    PH1: row,
    PH2: column
  });
};
const typeNameofCommandToRun = () => {
  return i18nString(TypeNameOfCommandToRun);
};
const showAndRunCommands = () => {
  return i18nString(ShowAndRunCommands);
};
const goToFile = () => {
  return i18nString(GoToFile);
};
const noResults = () => {
  return i18nString(NoResults);
};
const quickOpen = () => {
  return i18nString(QuickOpen);
};
const goToLineColumn = () => {
  return i18nString(GoToLineColumn);
};
const goToSymbolInEditor = () => {
  return i18nString(GoToSymbolInEditor);
};
const searchForText = () => {
  return i18nString(SearchForText);
};
const openView = () => {
  return i18nString(OpenView);
};

const getPicksGoToColumn = async value => {
  if (value === GoToColumn) {
    return getPicksGoToColumnBase();
  }
  if (value.startsWith(GoToColumn)) {
    const columnString = value.slice(GoToColumn.length);
    const wantedColumn = Number.parseInt(columnString, 10);
    if (Number.isNaN(wantedColumn)) {
      return getPicksGoToColumnBase();
    }
    const text = await getText();
    const position = getPosition(text, wantedColumn);
    return [{
      description: '',
      direntType: 0,
      fileIcon: '',
      icon: '',
      label: pressEnterToGoToLine(position.row, position.column),
      matches: [],
      uri: ''
    }];
  }
  return [];
};

const splitLines$2 = lines => {
  if (!lines) {
    return [];
  }
  return lines.split('\n');
};

const getPicksGoToLineBase = async () => {
  const text = await getText();
  const lines = splitLines$2(text);
  const lineCount = lines.length;
  return [{
    description: '',
    direntType: 0,
    fileIcon: '',
    icon: '',
    label: `Type a line number to go to (from 1 to ${lineCount})`,
    matches: [],
    uri: ''
  }];
};

const parseGotoline = value => {
  const lineString = value.slice(GoToLine$1.length);
  const wantedLine = Number.parseInt(lineString, 10);
  if (Number.isNaN(wantedLine)) {
    return -1;
  }
  return wantedLine;
};

const getPicks$8 = async value => {
  if (value === GoToLine$1) {
    return getPicksGoToLineBase();
  }
  if (value.startsWith(GoToLine$1)) {
    const wantedLine = parseGotoline(value);
    if (wantedLine === -1) {
      return getPicksGoToLineBase();
    }
    const rowIndex = wantedLine - 1;
    const columnIndex = 0;
    return [{
      description: '',
      direntType: 0,
      fileIcon: '',
      icon: '',
      label: pressEnterToGoToLine(rowIndex, columnIndex),
      matches: [],
      uri: ''
    }];
  }
  return [];
};

const DotDotDot = '...';
const Colon = ':';
const Percent = '%';
const AngleBracket = '>';
const View$1 = 'view';

const getPicks$7 = async () => {
  return [{
    description: goToFile(),
    direntType: None$2,
    fileIcon: '',
    icon: '',
    label: DotDotDot,
    matches: [],
    uri: ''
  }, {
    description: goToLineColumn(),
    direntType: None$2,
    fileIcon: '',
    icon: '',
    label: ':',
    matches: [],
    uri: ''
  }, {
    description: goToSymbolInEditor(),
    direntType: None$2,
    fileIcon: '',
    icon: '',
    label: Colon,
    matches: [],
    uri: ''
  }, {
    description: searchForText(),
    direntType: None$2,
    fileIcon: '',
    icon: '',
    label: Percent,
    matches: [],
    uri: ''
  }, {
    description: showAndRunCommands(),
    direntType: None$2,
    fileIcon: '',
    icon: '',
    label: AngleBracket,
    matches: [],
    uri: ''
  }, {
    description: openView(),
    direntType: None$2,
    fileIcon: '',
    icon: '',
    label: View$1,
    matches: [],
    uri: ''
  }];
};

const getRecentlyOpened = () => {
  return invoke$1(/* RecentlyOpened.getRecentlyOpened */'RecentlyOpened.getRecentlyOpened');
};

const getLabel = uri => {
  if (uri.startsWith('file://')) {
    return uri.slice('file://'.length);
  }
  return uri;
};
const toProtoVisibleItem = uri => {
  return {
    description: '',
    direntType: Directory,
    fileIcon: '',
    icon: '',
    label: getLabel(uri),
    matches: [],
    uri
  };
};
const getPicks$6 = async () => {
  const recentlyOpened = await getRecentlyOpened();
  const picks = recentlyOpened.map(toProtoVisibleItem);
  return picks;
};

const getPicks$5 = async () => {
  const picks = [];
  return picks;
};

const getPicks$4 = async () => {
  return [];
};

const getPicks$3 = async () => {
  const picks = [];
  return picks;
};

const Hide = 'hide';
const KeepOpen = '';

const selectPick$9 = async pick => {
  const id = pick.label;
  await setColorTheme(id);
  return {
    command: Hide
  };
};

const hideIds = ['AutoUpdater.checkForUpdates'];
const shouldHide = item => {
  if (hideIds.includes(item.id)) {
    return false;
  }
  if (item.id === 'Viewlet.openWidget' && item.args?.[0] === 'QuickPick') {
    return false;
  }
  return true;
};

const selectPickBuiltin = async item => {
  const args = item.args || [];
  // TODO ids should be all numbers for efficiency -> also directly can call command
  await invoke$1(item.id, ...args);
  if (shouldHide(item)) {
    return {
      command: Hide
    };
  }
  return {
    command: KeepOpen
  };
};
const selectPickExtension = async item => {
  const id = item.id.slice(4); // TODO lots of string allocation with 'ext.' find a better way to separate builtin commands from extension commands
  try {
    await invoke$1('ExtensionHost.executeCommand', id);
  } catch (error) {
    await handleError(error, false);
    await showErrorDialog(error);
  }
  return {
    command: Hide
  };
};
const selectPick$8 = async item => {
  // @ts-ignore
  const {
    id
  } = item;
  if (id.startsWith('ext.')) {
    return selectPickExtension(item);
  }
  return selectPickBuiltin(item);
};

const state = {
  args: []
};

const selectPick$7 = async pick => {
  const {
    args
  } = state;
  const resolveId = args[2];
  await invoke$1(`QuickPick.executeCallback`, resolveId, pick);
  return {
    command: Hide
  };
};

const openUri = async uri => {
  await openUri$1(uri);
};

const selectPick$6 = async pick => {
  const {
    description
  } = pick;
  const fileName = pick.label;
  const workspace = await getWorkspacePath();
  const absolutePath = `${workspace}/${description}/${fileName}`;
  await openUri(absolutePath);
  return {
    command: Hide
  };
};

const setCursor = async (rowIndex, columnIndex) => {
  await invoke$1('Editor.cursorSet', rowIndex, columnIndex);
};

const goToPositionAndFocus = async (rowIndex, columnIndex) => {
  await setCursor(rowIndex, columnIndex);
  await invoke$1('Editor.handleFocus');
};

const selectPickGoToColumn = async (item, value) => {
  if (value.startsWith(GoToColumn)) {
    const columnString = value.slice(2);
    const wantedColumn = Number.parseInt(columnString, 10);
    const text = await getText();
    const position = getPosition(text, wantedColumn);
    await goToPositionAndFocus(position.row, position.column);
    return {
      command: Hide
    };
  }
  return {
    command: Hide
  };
};

const selectPick$5 = async (item, value) => {
  if (value.startsWith(GoToLine$1)) {
    const lineString = value.slice(GoToLine$1.length);
    const wantedLine = Number.parseInt(lineString, 10);
    const rowIndex = wantedLine - 1;
    const columnIndex = 0;
    await goToPositionAndFocus(rowIndex, columnIndex);
    return {
      command: Hide
    };
  }
  return {
    command: Hide
  };
};

const selectPick$4 = async item => {
  // Command.execute(/* openView */ 549, /* viewName */ item.label)
  return {
    command: Hide
  };
};

const openWorkspaceFolder = uri => {
  return invoke$1(/* Workspace.setPath */'Workspace.setPath', /* path */uri);
};

// TODO selectPick should be independent of show/hide
const selectPick$3 = async pick => {
  const {
    uri
  } = pick;
  await openWorkspaceFolder(uri);
  return {
    command: Hide
  };
};

const selectPick$2 = async item => {
  return {
    command: Hide
  };
};

const selectPick$1 = async item => {
  // Command.execute(/* openView */ 549, /* viewName */ item.label)
  return {
    command: Hide
  };
};

const selectPick = async item => {
  return {
    command: Hide
  };
};

const selectPicks = [selectPick$9, selectPick$8, selectPick$7, selectPick$6, selectPickGoToColumn, selectPick$5, selectPick$4, selectPick$3, selectPick$2, selectPick$1, selectPick];
const getPicks$2 = [getPicks$c, getPicks$b, getPicks$a, getPicks$9, getPicksGoToColumn, getPicks$8, getPicks$7, getPicks$6, getPicks$5, getPicks$4, getPicks$3];

const select = selectPicks;
const getPick$1 = getPicks$2;
const getPicks$1 = id => {
  const fn = getPick$1[id];
  return fn;
};
const getSelect = id => {
  const fn = select[id];
  return fn;
};

const getPicks = (id, searchValue, args, {
  assetDir,
  platform
}) => {
  const fn = getPicks$1(id);
  return fn(searchValue, args, {
    assetDir,
    platform
  });
};

const getQuickPickSubProviderId = (id, prefix) => {
  if (id !== EveryThing$1) {
    return id;
  }
  switch (prefix) {
    case Command:
      return Commands$1;
    case GoToColumn:
      return GoToColumn$1;
    case GoToLine$1:
      return GoToLine$2;
    case Help$1:
      return Help$2;
    case Symbol$2:
      return Symbol$3;
    case View$2:
      return View$3;
    case WorkspaceSymbol$1:
      return WorkspaceSymbol$2;
    default:
      return File$2;
  }
};

// TODO when user types letters -> no need to query provider again -> just filter existing results
const setValue = async (state, newValue) => {
  const {
    args,
    assetDir,
    fileIconCache,
    height,
    itemHeight,
    maxLineY,
    minLineY,
    platform,
    providerId,
    value
  } = state;
  if (value === newValue) {
    return state;
  }
  const prefix = getQuickPickPrefix(newValue);
  const subId = getQuickPickSubProviderId(providerId, prefix);
  const newPicks = await getPicks(subId, newValue, args, {
    assetDir,
    platform
  });
  const filterValue = getFilterValue(providerId, subId, newValue);
  const items = filterQuickPickItems(newPicks, filterValue);
  const focusedIndex = items.length === 0 ? -1 : 0;
  const sliced = items.slice(minLineY, maxLineY);
  const {
    icons,
    newFileIconCache
  } = await getQuickPickFileIcons(sliced, fileIconCache);
  const listHeight = getListHeight(items.length, itemHeight, height);
  const finalDeltaY = getFinalDeltaY(listHeight, itemHeight, items.length);
  return {
    ...state,
    fileIconCache: newFileIconCache,
    finalDeltaY,
    focusedIndex,
    icons,
    inputSource: Script,
    items,
    picks: newPicks,
    value: newValue
  };
};

// TODO when user types letters -> no need to query provider again -> just filter existing results
const handleInput = async (state, newValue, cursorOffset, inputSource = Script) => {
  if (state.value === newValue) {
    return {
      ...state,
      cursorOffset,
      inputSource
    };
  }
  const newState = await setValue(state, newValue);
  return {
    ...newState,
    cursorOffset,
    inputSource
  };
};

const handleBeforeInput = (state, inputType, data, selectionStart, selectionEnd) => {
  string(inputType);
  number(selectionStart);
  number(selectionEnd);
  const {
    value
  } = state;
  const {
    cursorOffset,
    newValue
  } = getNewValue(value, inputType, data, selectionStart, selectionEnd);
  return handleInput(state, newValue, cursorOffset, User);
};

const handleBlur = async state => {
  // TODO fix virtual dom diffing so that input isn't destroyed and loses focus when rerendering
  // await CloseWidget.closeWidget(state.uid)
  return state;
};

const getIndex = (top, headerHeight, itemHeight, y) => {
  const relativeY = y - top - headerHeight;
  const index = Math.floor(relativeY / itemHeight);
  return index;
};

const getPick = (items, index) => {
  array(items);
  number(index);
  // if (index < state.recentPicks.length) {
  //   return state.recentPicks[index]
  // }
  // index -= state.recentPicks.length
  if (index < items.length) {
    return items[index];
  }
  console.warn('no pick matching index', index);
  return undefined;
};

const selectIndex = async (state, index, button = /* left */0) => {
  const {
    items,
    minLineY,
    providerId,
    value
  } = state;
  const actualIndex = index + minLineY;
  const pick = getPick(items, actualIndex);
  if (!pick) {
    return state;
  }
  const prefix = getQuickPickPrefix(value);
  const subId = getQuickPickSubProviderId(providerId, prefix);
  const fn = getSelect(subId);
  const selectPickResult = await fn(pick, value);
  object(selectPickResult);
  string(selectPickResult.command);
  const {
    command
  } = selectPickResult;
  switch (command) {
    case Hide:
      await closeWidget(state.uid);
      return state;
    default:
      return state;
  }

  // TODO recent picks should be per provider
  // if (!state.recentPickIds.has(pick.id)) {
  //   state.recentPicks.unshift(pick)
  //   state.recentPickIds.add(pick.id)
  // }
  // if (state.recentPicks.length > RECENT_PICKS_MAX_SIZE) {
  //   const last = state.recentPicks.pop()
  //   state.recentPickIds.delete(last.id)
  // }
};

const handleClickAt = (state, x, y) => {
  const {
    headerHeight,
    itemHeight,
    top
  } = state;
  const index = getIndex(top, headerHeight, itemHeight, y);
  return selectIndex(state, index);
};

const handleFocus = async state => {
  // TODO fix virtual dom diffing so that input isn't destroyed and loses focus when rerendering
  await setFocus(FocusQuickPickInput);
  // await CloseWidget.closeWidget(state.uid)
  return state;
};

const isMessagePort = value => {
  return value && value instanceof MessagePort;
};
const isMessagePortMain = value => {
  return value && value.constructor && value.constructor.name === 'MessagePortMain';
};
const isOffscreenCanvas = value => {
  return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
};
const isInstanceOf = (value, constructorName) => {
  return value?.constructor?.name === constructorName;
};
const isSocket = value => {
  return isInstanceOf(value, 'Socket');
};
const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
const isTransferrable = value => {
  for (const fn of transferrables) {
    if (fn(value)) {
      return true;
    }
  }
  return false;
};
const walkValue = (value, transferrables, isTransferrable) => {
  if (!value) {
    return;
  }
  if (isTransferrable(value)) {
    transferrables.push(value);
    return;
  }
  if (Array.isArray(value)) {
    for (const item of value) {
      walkValue(item, transferrables, isTransferrable);
    }
    return;
  }
  if (typeof value === 'object') {
    for (const property of Object.values(value)) {
      walkValue(property, transferrables, isTransferrable);
    }
    return;
  }
};
const getTransferrables = value => {
  const transferrables = [];
  walkValue(value, transferrables, isTransferrable);
  return transferrables;
};
const attachEvents = that => {
  const handleMessage = (...args) => {
    const data = that.getData(...args);
    that.dispatchEvent(new MessageEvent('message', {
      data
    }));
  };
  that.onMessage(handleMessage);
  const handleClose = event => {
    that.dispatchEvent(new Event('close'));
  };
  that.onClose(handleClose);
};
class Ipc extends EventTarget {
  constructor(rawIpc) {
    super();
    this._rawIpc = rawIpc;
    attachEvents(this);
  }
}
const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
const NewLine$1 = '\n';
const joinLines$1 = lines => {
  return lines.join(NewLine$1);
};
const RE_AT = /^\s+at/;
const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
const isNormalStackLine = line => {
  return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
};
const getDetails = lines => {
  const index = lines.findIndex(isNormalStackLine);
  if (index === -1) {
    return {
      actualMessage: joinLines$1(lines),
      rest: []
    };
  }
  let lastIndex = index - 1;
  while (++lastIndex < lines.length) {
    if (!isNormalStackLine(lines[lastIndex])) {
      break;
    }
  }
  return {
    actualMessage: lines[index - 1],
    rest: lines.slice(index, lastIndex)
  };
};
const splitLines$1 = lines => {
  return lines.split(NewLine$1);
};
const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
const isMessageCodeBlockStartIndex = line => {
  return RE_MESSAGE_CODE_BLOCK_START.test(line);
};
const isMessageCodeBlockEndIndex = line => {
  return RE_MESSAGE_CODE_BLOCK_END.test(line);
};
const getMessageCodeBlock = stderr => {
  const lines = splitLines$1(stderr);
  const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
  const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
  const relevantLines = lines.slice(startIndex, endIndex);
  const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
  return relevantMessage;
};
const isModuleNotFoundMessage = line => {
  return line.includes('[ERR_MODULE_NOT_FOUND]');
};
const getModuleNotFoundError = stderr => {
  const lines = splitLines$1(stderr);
  const messageIndex = lines.findIndex(isModuleNotFoundMessage);
  const message = lines[messageIndex];
  return {
    code: ERR_MODULE_NOT_FOUND,
    message
  };
};
const isModuleNotFoundError = stderr => {
  if (!stderr) {
    return false;
  }
  return stderr.includes('ERR_MODULE_NOT_FOUND');
};
const isModulesSyntaxError = stderr => {
  if (!stderr) {
    return false;
  }
  return stderr.includes('SyntaxError: Cannot use import statement outside a module');
};
const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
const isUnhelpfulNativeModuleError = stderr => {
  return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
};
const getNativeModuleErrorMessage = stderr => {
  const message = getMessageCodeBlock(stderr);
  return {
    code: E_INCOMPATIBLE_NATIVE_MODULE,
    message: `Incompatible native node module: ${message}`
  };
};
const getModuleSyntaxError = () => {
  return {
    code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON,
    message: `ES Modules are not supported in electron`
  };
};
const getHelpfulChildProcessError = (stdout, stderr) => {
  if (isUnhelpfulNativeModuleError(stderr)) {
    return getNativeModuleErrorMessage(stderr);
  }
  if (isModulesSyntaxError(stderr)) {
    return getModuleSyntaxError();
  }
  if (isModuleNotFoundError(stderr)) {
    return getModuleNotFoundError(stderr);
  }
  const lines = splitLines$1(stderr);
  const {
    actualMessage,
    rest
  } = getDetails(lines);
  return {
    code: '',
    message: actualMessage,
    stack: rest
  };
};
class IpcError extends VError {
  // @ts-ignore
  constructor(betterMessage, stdout = '', stderr = '') {
    if (stdout || stderr) {
      // @ts-ignore
      const {
        code,
        message,
        stack
      } = getHelpfulChildProcessError(stdout, stderr);
      const cause = new Error(message);
      // @ts-ignore
      cause.code = code;
      cause.stack = stack;
      super(cause, betterMessage);
    } else {
      super(betterMessage);
    }
    // @ts-ignore
    this.name = 'IpcError';
    // @ts-ignore
    this.stdout = stdout;
    // @ts-ignore
    this.stderr = stderr;
  }
}
const readyMessage = 'ready';
const getData$2 = event => {
  return event.data;
};
const listen$7 = () => {
  // @ts-ignore
  if (typeof WorkerGlobalScope === 'undefined') {
    throw new TypeError('module is not in web worker scope');
  }
  return globalThis;
};
const signal$8 = global => {
  global.postMessage(readyMessage);
};
class IpcChildWithModuleWorker extends Ipc {
  getData(event) {
    return getData$2(event);
  }
  send(message) {
    // @ts-ignore
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    // @ts-ignore
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    // ignore
  }
  onClose(callback) {
    // ignore
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
}
const wrap$f = global => {
  return new IpcChildWithModuleWorker(global);
};
const waitForFirstMessage = async port => {
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  port.addEventListener('message', resolve, {
    once: true
  });
  const event = await promise;
  // @ts-ignore
  return event.data;
};
const listen$6 = async () => {
  const parentIpcRaw = listen$7();
  signal$8(parentIpcRaw);
  const parentIpc = wrap$f(parentIpcRaw);
  const firstMessage = await waitForFirstMessage(parentIpc);
  if (firstMessage.method !== 'initialize') {
    throw new IpcError('unexpected first message');
  }
  const type = firstMessage.params[0];
  if (type === 'message-port') {
    parentIpc.send({
      id: firstMessage.id,
      jsonrpc: '2.0',
      result: null
    });
    parentIpc.dispose();
    const port = firstMessage.params[1];
    return port;
  }
  return globalThis;
};
class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
  getData(event) {
    return getData$2(event);
  }
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    if (this._rawIpc.close) {
      this._rawIpc.close();
    }
  }
  onClose(callback) {
    // ignore
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
    this._rawIpc.start();
  }
}
const wrap$e = port => {
  return new IpcChildWithModuleWorkerAndMessagePort(port);
};
const IpcChildWithModuleWorkerAndMessagePort$1 = {
  __proto__: null,
  listen: listen$6,
  wrap: wrap$e
};
const addListener = (emitter, type, callback) => {
  if ('addEventListener' in emitter) {
    emitter.addEventListener(type, callback);
  } else {
    emitter.on(type, callback);
  }
};
const removeListener = (emitter, type, callback) => {
  if ('removeEventListener' in emitter) {
    emitter.removeEventListener(type, callback);
  } else {
    emitter.off(type, callback);
  }
};
const getFirstEvent = (eventEmitter, eventMap) => {
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  const listenerMap = Object.create(null);
  const cleanup = value => {
    for (const event of Object.keys(eventMap)) {
      removeListener(eventEmitter, event, listenerMap[event]);
    }
    resolve(value);
  };
  for (const [event, type] of Object.entries(eventMap)) {
    const listener = event => {
      cleanup({
        event,
        type
      });
    };
    addListener(eventEmitter, event, listener);
    listenerMap[event] = listener;
  }
  return promise;
};
const Message$1 = 3;
const create$5$1 = async ({
  isMessagePortOpen,
  messagePort
}) => {
  if (!isMessagePort(messagePort)) {
    throw new IpcError('port must be of type MessagePort');
  }
  if (isMessagePortOpen) {
    return messagePort;
  }
  const eventPromise = getFirstEvent(messagePort, {
    message: Message$1
  });
  messagePort.start();
  const {
    event,
    type
  } = await eventPromise;
  if (type !== Message$1) {
    throw new IpcError('Failed to wait for ipc message');
  }
  if (event.data !== readyMessage) {
    throw new IpcError('unexpected first message');
  }
  return messagePort;
};
const signal$1 = messagePort => {
  messagePort.start();
};
class IpcParentWithMessagePort extends Ipc {
  getData = getData$2;
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    this._rawIpc.close();
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
  onClose(callback) {}
}
const wrap$5 = messagePort => {
  return new IpcParentWithMessagePort(messagePort);
};
const IpcParentWithMessagePort$1 = {
  __proto__: null,
  create: create$5$1,
  signal: signal$1,
  wrap: wrap$5
};

const Two$1 = '2.0';
const callbacks$1 = Object.create(null);
const get = id => {
  return callbacks$1[id];
};
const remove = id => {
  delete callbacks$1[id];
};
class JsonRpcError extends Error {
  constructor(message) {
    super(message);
    this.name = 'JsonRpcError';
  }
}
const NewLine = '\n';
const DomException = 'DOMException';
const ReferenceError$1 = 'ReferenceError';
const SyntaxError$1 = 'SyntaxError';
const TypeError$1 = 'TypeError';
const getErrorConstructor = (message, type) => {
  if (type) {
    switch (type) {
      case DomException:
        return DOMException;
      case ReferenceError$1:
        return ReferenceError;
      case SyntaxError$1:
        return SyntaxError;
      case TypeError$1:
        return TypeError;
      default:
        return Error;
    }
  }
  if (message.startsWith('TypeError: ')) {
    return TypeError;
  }
  if (message.startsWith('SyntaxError: ')) {
    return SyntaxError;
  }
  if (message.startsWith('ReferenceError: ')) {
    return ReferenceError;
  }
  return Error;
};
const constructError = (message, type, name) => {
  const ErrorConstructor = getErrorConstructor(message, type);
  if (ErrorConstructor === DOMException && name) {
    return new ErrorConstructor(message, name);
  }
  if (ErrorConstructor === Error) {
    const error = new Error(message);
    if (name && name !== 'VError') {
      error.name = name;
    }
    return error;
  }
  return new ErrorConstructor(message);
};
const joinLines = lines => {
  return lines.join(NewLine);
};
const splitLines = lines => {
  return lines.split(NewLine);
};
const getCurrentStack = () => {
  const stackLinesToSkip = 3;
  const currentStack = joinLines(splitLines(new Error().stack || '').slice(stackLinesToSkip));
  return currentStack;
};
const getNewLineIndex = (string, startIndex = undefined) => {
  return string.indexOf(NewLine, startIndex);
};
const getParentStack = error => {
  let parentStack = error.stack || error.data || error.message || '';
  if (parentStack.startsWith('    at')) {
    parentStack = error.message + NewLine + parentStack;
  }
  return parentStack;
};
const MethodNotFound = -32601;
const Custom$1 = -32001;
const restoreJsonRpcError = error => {
  const currentStack = getCurrentStack();
  if (error && error instanceof Error) {
    if (typeof error.stack === 'string') {
      error.stack = error.stack + NewLine + currentStack;
    }
    return error;
  }
  if (error && error.code && error.code === MethodNotFound) {
    const restoredError = new JsonRpcError(error.message);
    const parentStack = getParentStack(error);
    restoredError.stack = parentStack + NewLine + currentStack;
    return restoredError;
  }
  if (error && error.message) {
    const restoredError = constructError(error.message, error.type, error.name);
    if (error.data) {
      if (error.data.stack && error.data.type && error.message) {
        restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
      } else if (error.data.stack) {
        restoredError.stack = error.data.stack;
      }
      if (error.data.codeFrame) {
        // @ts-ignore
        restoredError.codeFrame = error.data.codeFrame;
      }
      if (error.data.code) {
        // @ts-ignore
        restoredError.code = error.data.code;
      }
      if (error.data.type) {
        // @ts-ignore
        restoredError.name = error.data.type;
      }
    } else {
      if (error.stack) {
        const lowerStack = restoredError.stack || '';
        // @ts-ignore
        const indexNewLine = getNewLineIndex(lowerStack);
        const parentStack = getParentStack(error);
        // @ts-ignore
        restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
      }
      if (error.codeFrame) {
        // @ts-ignore
        restoredError.codeFrame = error.codeFrame;
      }
    }
    return restoredError;
  }
  if (typeof error === 'string') {
    return new Error(`JsonRpc Error: ${error}`);
  }
  return new Error(`JsonRpc Error: ${error}`);
};
const unwrapJsonRpcResult = responseMessage => {
  if ('error' in responseMessage) {
    const restoredError = restoreJsonRpcError(responseMessage.error);
    throw restoredError;
  }
  if ('result' in responseMessage) {
    return responseMessage.result;
  }
  throw new JsonRpcError('unexpected response message');
};
const warn = (...args) => {
  console.warn(...args);
};
const resolve = (id, response) => {
  const fn = get(id);
  if (!fn) {
    console.log(response);
    warn(`callback ${id} may already be disposed`);
    return;
  }
  fn(response);
  remove(id);
};
const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
const getErrorType = prettyError => {
  if (prettyError && prettyError.type) {
    return prettyError.type;
  }
  if (prettyError && prettyError.constructor && prettyError.constructor.name) {
    return prettyError.constructor.name;
  }
  return undefined;
};
const isAlreadyStack = line => {
  return line.trim().startsWith('at ');
};
const getStack = prettyError => {
  const stackString = prettyError.stack || '';
  const newLineIndex = stackString.indexOf('\n');
  if (newLineIndex !== -1 && !isAlreadyStack(stackString.slice(0, newLineIndex))) {
    return stackString.slice(newLineIndex + 1);
  }
  return stackString;
};
const getErrorProperty = (error, prettyError) => {
  if (error && error.code === E_COMMAND_NOT_FOUND) {
    return {
      code: MethodNotFound,
      data: error.stack,
      message: error.message
    };
  }
  return {
    code: Custom$1,
    data: {
      code: prettyError.code,
      codeFrame: prettyError.codeFrame,
      name: prettyError.name,
      stack: getStack(prettyError),
      type: getErrorType(prettyError)
    },
    message: prettyError.message
  };
};
const create$1$1 = (id, error) => {
  return {
    error,
    id,
    jsonrpc: Two$1
  };
};
const getErrorResponse = (id, error, preparePrettyError, logError) => {
  const prettyError = preparePrettyError(error);
  logError(error, prettyError);
  const errorProperty = getErrorProperty(error, prettyError);
  return create$1$1(id, errorProperty);
};
const create$7 = (message, result) => {
  return {
    id: message.id,
    jsonrpc: Two$1,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$7(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
  return {
    error: {
      code: Custom$1,
      data: error,
      // @ts-ignore
      message: error.message
    },
    id,
    jsonrpc: Two$1
  };
};
const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
  try {
    const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
    return getSuccessResponse(message, result);
  } catch (error) {
    if (ipc.canUseSimpleErrorResponse) {
      return getErrorResponseSimple(message.id, error);
    }
    return getErrorResponse(message.id, error, preparePrettyError, logError);
  }
};
const defaultPreparePrettyError = error => {
  return error;
};
const defaultLogError = () => {
  // ignore
};
const defaultRequiresSocket = () => {
  return false;
};
const defaultResolve = resolve;

// TODO maybe remove this in v6 or v7, only accept options object to simplify the code
const normalizeParams = args => {
  if (args.length === 1) {
    const options = args[0];
    return {
      execute: options.execute,
      ipc: options.ipc,
      logError: options.logError || defaultLogError,
      message: options.message,
      preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
      requiresSocket: options.requiresSocket || defaultRequiresSocket,
      resolve: options.resolve || defaultResolve
    };
  }
  return {
    execute: args[2],
    ipc: args[0],
    logError: args[5],
    message: args[1],
    preparePrettyError: args[4],
    requiresSocket: args[6],
    resolve: args[3]
  };
};
const handleJsonRpcMessage = async (...args) => {
  const options = normalizeParams(args);
  const {
    execute,
    ipc,
    logError,
    message,
    preparePrettyError,
    requiresSocket,
    resolve
  } = options;
  if ('id' in message) {
    if ('method' in message) {
      const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
      try {
        ipc.send(response);
      } catch (error) {
        const errorResponse = getErrorResponse(message.id, error, preparePrettyError, logError);
        ipc.send(errorResponse);
      }
      return;
    }
    resolve(message.id, message);
    return;
  }
  if ('method' in message) {
    await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
    return;
  }
  throw new JsonRpcError('unexpected message');
};

const Two = '2.0';

const create$6 = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};

const create$5 = (id, method, params) => {
  const message = {
    id,
    jsonrpc: Two,
    method,
    params
  };
  return message;
};

let id = 0;
const create$4 = () => {
  return ++id;
};

const registerPromise = map => {
  const id = create$4();
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  map[id] = resolve;
  return {
    id,
    promise
  };
};

const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
  const {
    id,
    promise
  } = registerPromise(callbacks);
  const message = create$5(id, method, params);
  if (useSendAndTransfer && ipc.sendAndTransfer) {
    ipc.sendAndTransfer(message);
  } else {
    ipc.send(message);
  }
  const responseMessage = await promise;
  return unwrapJsonRpcResult(responseMessage);
};
const createRpc = ipc => {
  const callbacks = Object.create(null);
  ipc._resolve = (id, response) => {
    const fn = callbacks[id];
    if (!fn) {
      console.warn(`callback ${id} may already be disposed`);
      return;
    }
    fn(response);
    delete callbacks[id];
  };
  const rpc = {
    async dispose() {
      await ipc?.dispose();
    },
    invoke(method, ...params) {
      return invokeHelper(callbacks, ipc, method, params, false);
    },
    invokeAndTransfer(method, ...params) {
      return invokeHelper(callbacks, ipc, method, params, true);
    },
    // @ts-ignore
    ipc,
    /**
     * @deprecated
     */
    send(method, ...params) {
      const message = create$6(method, params);
      ipc.send(message);
    }
  };
  return rpc;
};

const requiresSocket = () => {
  return false;
};
const preparePrettyError = error => {
  return error;
};
const logError = () => {
  // handled by renderer worker
};
const handleMessage = event => {
  const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
  const actualExecute = event?.target?.execute || execute;
  return handleJsonRpcMessage(event.target, event.data, actualExecute, event.target._resolve, preparePrettyError, logError, actualRequiresSocket);
};

const handleIpc = ipc => {
  if ('addEventListener' in ipc) {
    ipc.addEventListener('message', handleMessage);
  } else if ('on' in ipc) {
    // deprecated
    ipc.on('message', handleMessage);
  }
};

const listen$1 = async (module, options) => {
  const rawIpc = await module.listen(options);
  if (module.signal) {
    module.signal(rawIpc);
  }
  const ipc = module.wrap(rawIpc);
  return ipc;
};

const create$3 = async ({
  commandMap,
  isMessagePortOpen = true,
  messagePort
}) => {
  // TODO create a commandMap per rpc instance
  register$1(commandMap);
  const rawIpc = await IpcParentWithMessagePort$1.create({
    isMessagePortOpen,
    messagePort
  });
  const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  messagePort.start();
  return rpc;
};

const create$2 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  const {
    port1,
    port2
  } = new MessageChannel();
  await send(port1);
  return create$3({
    commandMap,
    isMessagePortOpen,
    messagePort: port2
  });
};

const createSharedLazyRpc = factory => {
  let rpcPromise;
  const getOrCreate = () => {
    if (!rpcPromise) {
      rpcPromise = factory();
    }
    return rpcPromise;
  };
  return {
    async dispose() {
      const rpc = await getOrCreate();
      await rpc.dispose();
    },
    async invoke(method, ...params) {
      const rpc = await getOrCreate();
      return rpc.invoke(method, ...params);
    },
    async invokeAndTransfer(method, ...params) {
      const rpc = await getOrCreate();
      return rpc.invokeAndTransfer(method, ...params);
    },
    async send(method, ...params) {
      const rpc = await getOrCreate();
      rpc.send(method, ...params);
    }
  };
};

const create$1 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  return createSharedLazyRpc(() => {
    return create$2({
      commandMap,
      isMessagePortOpen,
      send
    });
  });
};

const create = async ({
  commandMap
}) => {
  // TODO create a commandMap per rpc instance
  register$1(commandMap);
  const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  return rpc;
};

const commandMapRef = {};

const handleMessagePort = async port => {
  await create$3({
    commandMap: commandMapRef,
    isMessagePortOpen: true,
    messagePort: port
  });
};

const initialize = async () => {
  // not needed anymore
};

const getDefaultValue = id => {
  switch (id) {
    case EveryThing$1:
      return Command;
    default:
      return '';
  }
};

const Commands = 'quickPick://commands';
const EveryThing = 'quickPick://everything';
const Recent = 'quickPick://recent';
const ColorTheme = 'quickPick://color-theme';
const Symbol$1 = 'quickPick://symbol';
const View = 'quickPick://view';
const Help = 'quickPick://help';
const WorkspaceSymbol = 'quickPick://workspace-symbol';
const Custom = 'quickPick://custom';
const GoToLine = 'quickPick://go-to-line';

const getQuickPickProviderId = prefix => {
  switch (prefix) {
    case ColorTheme:
      return ColorTheme$1;
    case Commands:
    case EveryThing:
    case GoToLine:
    case Help:
    case Symbol$1:
    case View:
    case WorkspaceSymbol:
      return EveryThing$1;
    case Custom:
      return Custom$2;
    case Recent:
      return Recent$1;
    default:
      return File$2;
  }
};

const parseArgs = (subId, args) => {
  if (subId !== Custom$2) {
    return {
      ignoreFocusOut: false,
      initialValue: ''
    };
  }
  const last = args.at(-1);
  if (!last || typeof last !== 'object') {
    return {
      ignoreFocusOut: false,
      initialValue: ''
    };
  }
  return {
    // @ts-ignore
    ignoreFocusOut: Boolean(last.ignoreFocusOut),
    // @ts-ignore
    initialValue: String(last.initialValue)
  };
};
const loadContent = async state => {
  const {
    args,
    assetDir,
    fileIconCache,
    height,
    itemHeight,
    maxVisibleItems,
    platform,
    uri
  } = state;
  const id = getQuickPickProviderId(uri);
  const value = getDefaultValue(id);
  const prefix = getQuickPickPrefix(value);
  const subId = getQuickPickSubProviderId(id, prefix);
  const newPicks = await getPicks(subId, value, args, {
    assetDir,
    platform
  });
  array(newPicks);
  const filterValue = getFilterValue(id, subId, value);
  const items = filterQuickPickItems(newPicks, filterValue);
  const minLineY = 0;
  const maxLineY = Math.min(minLineY + maxVisibleItems, newPicks.length);
  const sliced = newPicks.slice(minLineY, maxLineY);
  const {
    icons,
    newFileIconCache
  } = await getQuickPickFileIcons(sliced, fileIconCache);
  const listHeight = getListHeight(items.length, itemHeight, height);
  const finalDeltaY = getFinalDeltaY(listHeight, itemHeight, items.length);
  const parsedArgs = parseArgs(subId, args);
  const finalValue = parsedArgs.initialValue || value;
  return {
    ...state,
    args,
    cursorOffset: value.length,
    fileIconCache: newFileIconCache,
    finalDeltaY,
    focused: true,
    focusedIndex: 0,
    icons,
    inputSource: Script,
    items,
    maxLineY,
    minLineY,
    picks: newPicks,
    placeholder: '',
    providerId: id,
    state: Finished,
    value: finalValue
  };
};

const callbacks = Object.create(null);
const executeCallback = id => {
  const fn = callbacks[id];
  delete callbacks[id];
  fn();
};

const getVisible$1 = (items, minLineY, maxLineY, icons) => {
  const range = items.slice(minLineY, maxLineY);
  const protoVisibleItems = range.map((item, index) => {
    return {
      ...item,
      fileIcon: icons[index]
    };
  });
  return protoVisibleItems;
};

const getScrollBarSize = (size, contentSize, minimumSliderSize) => {
  if (size >= contentSize) {
    return 0;
  }
  return Math.max(Math.round(size ** 2 / contentSize), minimumSliderSize);
};

const emptyHighlightSections = [];

const getHighlightSections = (highlights, label) => {
  if (highlights.length === 0) {
    return emptyHighlightSections;
  }
  const sections = [];
  let position = 0;
  for (let i = 0; i < highlights.length; i += 2) {
    const highlightStart = highlights[i];
    const highlightEnd = highlights[i + 1];
    if (position < highlightStart) {
      const beforeText = label.slice(position, highlightStart);
      sections.push({
        highlighted: false,
        text: beforeText
      });
    }
    const highlightText = label.slice(highlightStart, highlightEnd);
    sections.push({
      highlighted: true,
      text: highlightText
    });
    position = highlightEnd;
  }
  if (position < label.length) {
    const afterText = label.slice(position);
    sections.push({
      highlighted: false,
      text: afterText
    });
  }
  return sections;
};

const getVisible = (setSize, protoVisibleItems, minLineY, focusedIndex) => {
  const visibleItems = protoVisibleItems.map((visibleItem, i) => {
    const highlights = visibleItem.matches.slice(1);
    const sections = getHighlightSections(highlights, visibleItem.label);
    return {
      ...visibleItem,
      highlights: sections,
      isActive: i === focusedIndex,
      posInSet: minLineY + i + 1,
      setSize
    };
  });
  return visibleItems;
};

const getScrollBarOffset = (delta, finalDelta, size, scrollBarSize) => {
  const scrollBarOffset = delta / finalDelta * (size - scrollBarSize);
  return scrollBarOffset;
};
const getScrollBarY = getScrollBarOffset;

const createQuickPickViewModel = (oldState, newState) => {
  const {
    cursorOffset,
    deltaY,
    finalDeltaY,
    focused,
    focusedIndex,
    headerHeight,
    height,
    icons,
    itemHeight,
    items,
    maxLineY,
    minimumSliderSize,
    minLineY,
    uid,
    value
  } = newState;
  const protoVisibleItems = getVisible$1(items, minLineY, maxLineY, icons);
  const visibleItems = getVisible(items.length, protoVisibleItems, minLineY, focusedIndex);
  const oldFocusedIndex = oldState.focusedIndex - oldState.minLineY;
  const newFocusedIndex = focusedIndex - minLineY;
  const itemCount = items.length;
  const listHeight = getListHeight(itemCount, itemHeight, height);
  const contentHeight = itemCount * itemHeight;
  const scrollBarHeight = getScrollBarSize(listHeight, contentHeight, minimumSliderSize);
  const scrollBarY = getScrollBarY(deltaY, finalDeltaY, height - headerHeight, scrollBarHeight);
  const roundedScrollBarY = Math.round(scrollBarY);
  return {
    cursorOffset,
    focused,
    height,
    newFocusedIndex,
    oldFocusedIndex,
    scrollBarHeight,
    scrollBarTop: roundedScrollBarY,
    uid,
    value,
    visibleItems
  };
};

const SetCursorOffset = 'setCursorOffset';
const SetFocusedIndex = 'setFocusedIndex';
const SetItemsHeight = 'setItemsHeight';

const renderCursorOffset = newState => {
  return ['Viewlet.send', newState.uid, /* method */SetCursorOffset, /* cursorOffset */newState.cursorOffset];
};

const QuickPickInput = 'QuickPickInput';

const renderFocus = newState => {
  return ['Viewlet.focusElementByName', QuickPickInput];
};

const renderFocusedIndex = newState => {
  return ['Viewlet.send', newState.uid, /* method */SetFocusedIndex, /* oldFocusedIndex */newState.oldFocusedIndex, /* newFocusedIndex */newState.newFocusedIndex];
};

const renderHeight = newState => {
  const {
    height,
    uid
  } = newState;
  if (height === 0) {
    return ['Viewlet.send', uid, /* method */SetItemsHeight, /* height */20];
  }
  return ['Viewlet.send', uid, /* method */SetItemsHeight, /* height */height];
};

const mergeClassNames = (...classNames) => {
  return classNames.filter(Boolean).join(' ');
};

const px = value => {
  return `${value}px`;
};
const position = (x, y) => {
  return `${x}px ${y}px`;
};

const text = data => {
  return {
    childCount: 0,
    text: data,
    type: Text
  };
};

const ComboBox = 'combobox';
const ListBox = 'listbox';
const None = 'none';
const Option = 'option';

const ContainContent = 'ContainContent';
const FileIcon = 'FileIcon';
const InputBox = 'InputBox';
const Label = 'Label';
const List = 'List';
const ListItems = 'ListItems';
const MaskIcon = 'MaskIcon';
const QuickPick$1 = 'QuickPick';
const QuickPickHeader = 'QuickPickHeader';
const QuickPickHighlight = 'QuickPickHighlight';
const QuickPickItem = 'QuickPickItem';
const QuickPickItemActive$1 = 'QuickPickItemActive';
const QuickPickItemDescription = 'QuickPickItemDescription';
const QuickPickItemLabel = 'QuickPickItemLabel';
const QuickPickMaskIcon = 'QuickPickMaskIcon';
const QuickPickStatus = 'QuickPickStatus';
const ScrollBar = 'ScrollBar';
const ScrollBarSmall = 'ScrollBarSmall';
const ScrollBarThumb = 'ScrollBarThumb';
const Viewlet = 'Viewlet';

const HandleWheel = 'handleWheel';
const HandlePointerDown = 'handlePointerDown';
const HandleBeforeInput = 'handleBeforeInput';
const HandleBlur = 'handleBlur';
const HandleFocus = 'handleFocus';
const HandleInput = 'handleInput';

const QuickPick = 'QuickPick';
const QuickPickItems = 'QuickPickItems';
const QuickPickItemActive = 'QuickPickItemActive';

const getQuickPickInputVirtualDom = () => {
  const ariaLabel = typeNameofCommandToRun();
  return {
    ariaAutoComplete: 'list',
    ariaExpanded: true,
    ariaLabel: ariaLabel,
    autocapitalize: 'off',
    autocomplete: 'off',
    childCount: 0,
    className: InputBox,
    inputType: 'text',
    name: QuickPickInput,
    onBeforeInput: HandleBeforeInput,
    onBlur: HandleBlur,
    onFocus: HandleFocus,
    onInput: HandleInput,
    role: ComboBox,
    spellcheck: false,
    type: Input
  };
};

const getQuickPickHeaderVirtualDom = () => {
  return [{
    childCount: 1,
    className: QuickPickHeader,
    type: Div
  }, getQuickPickInputVirtualDom()];
};

const getFileIconVirtualDom = icon => {
  return {
    childCount: 0,
    className: FileIcon,
    role: None,
    src: icon,
    type: Img
  };
};

const quickPickHighlight = {
  childCount: 1,
  className: QuickPickHighlight,
  type: Span
};
const getHighlights = (sections, label) => {
  const labelDom = {
    childCount: 0,
    className: QuickPickItemLabel,
    type: Div
  };
  const nodes = [labelDom];
  if (sections.length === 0) {
    labelDom.childCount++;
    nodes.push(text(label));
  } else {
    for (const section of sections) {
      if (section.highlighted) {
        labelDom.childCount++;
        nodes.push(quickPickHighlight, text(section.text));
      } else {
        labelDom.childCount++;
        nodes.push(text(section.text));
      }
    }
  }
  return nodes;
};

const getQuickPickItemVirtualDom = visibleItem => {
  const {
    description,
    fileIcon,
    highlights,
    icon,
    isActive,
    label,
    posInSet,
    setSize
  } = visibleItem;
  const dom = [{
    ariaPosInSet: posInSet,
    ariaSetSize: setSize,
    childCount: 1,
    className: QuickPickItem,
    role: Option,
    type: Div
  }];
  const parent = dom[0];
  if (isActive) {
    parent.id = QuickPickItemActive;
    parent.className += ' ' + QuickPickItemActive$1;
  }
  if (fileIcon) {
    parent.childCount++;
    dom.push(getFileIconVirtualDom(fileIcon));
  } else if (icon) {
    parent.childCount++;
    dom.push({
      childCount: 0,
      className: mergeClassNames(QuickPickMaskIcon, MaskIcon, `MaskIcon${icon}`),
      type: Div
    });
  }
  const highlightDom = getHighlights(highlights, label);
  dom.push(...highlightDom);
  if (description) {
    parent.childCount++;
    dom.push({
      childCount: 1,
      className: QuickPickItemDescription,
      type: Div
    }, text(description));
  }
  return dom;
};

const getQuickPickNoResultsVirtualDom = () => {
  const noResults$1 = noResults();
  return [{
    childCount: 1,
    className: mergeClassNames(QuickPickItem, QuickPickItemActive$1, QuickPickStatus),
    type: Div
  }, {
    childCount: 1,
    className: Label,
    type: Div
  }, text(noResults$1)];
};

const getQuickPickItemsVirtualDom = visibleItems => {
  if (visibleItems.length === 0) {
    return getQuickPickNoResultsVirtualDom();
  }
  const dom = visibleItems.flatMap(getQuickPickItemVirtualDom);
  return dom;
};

const getScrollBarVirtualDom = (scrollBarHeight, scrollBarTop) => {
  const shouldShowScrollbar = scrollBarHeight > 0;
  if (!shouldShowScrollbar) {
    return [];
  }
  const heightString = px(scrollBarHeight);
  const translateString = position(0, scrollBarTop);
  return [{
    childCount: 1,
    className: mergeClassNames(ScrollBar, ScrollBarSmall),
    type: Div
  }, {
    childCount: 0,
    className: ScrollBarThumb,
    height: heightString,
    translate: translateString,
    type: Div
  }];
};

const getQuickPickVirtualDom = (visibleItems, scrollBarHeight, scrollBarTop) => {
  const quickOpen$1 = quickOpen();
  const shouldShowScrollbar = scrollBarHeight > 0;
  return [{
    ariaLabel: quickOpen$1,
    childCount: 2,
    className: mergeClassNames(Viewlet, QuickPick$1),
    id: QuickPick,
    type: Div
  }, ...getQuickPickHeaderVirtualDom(), {
    ariaActivedescendant: QuickPickItemActive,
    childCount: shouldShowScrollbar ? 2 : 1,
    className: mergeClassNames(List, ContainContent),
    id: QuickPickItems,
    onPointerDown: HandlePointerDown,
    onWheel: HandleWheel,
    role: ListBox,
    type: Div
  }, {
    childCount: visibleItems.length,
    className: mergeClassNames(ListItems, ContainContent),
    type: Div
  }, ...getQuickPickItemsVirtualDom(visibleItems), ...getScrollBarVirtualDom(scrollBarHeight, scrollBarTop)];
};

const renderItems = newState => {
  const {
    scrollBarHeight,
    scrollBarTop,
    visibleItems
  } = newState;
  const dom = getQuickPickVirtualDom(visibleItems, scrollBarHeight, scrollBarTop);
  return [SetDom2, dom];
};

const renderValue = newState => {
  return ['Viewlet.setValueByName', QuickPickInput, /* value */newState.value];
};

const getRenderer = diffType => {
  switch (diffType) {
    case Height:
      return renderHeight;
    case RenderCursorOffset:
      return renderCursorOffset;
    case RenderFocus:
      return renderFocus;
    case RenderFocusedIndex:
      return renderFocusedIndex;
    case RenderItems:
      return renderItems;
    case RenderValue:
      return renderValue;
    default:
      throw new Error('unknown renderer');
  }
};

const applyRender = (oldState, newState, diffResult) => {
  const commands = [];
  const viewModel = createQuickPickViewModel(oldState, newState);
  for (const item of diffResult) {
    if (item === Height) {
      continue;
    }
    if (item === RenderFocusedIndex) {
      continue;
    }
    const fn = getRenderer(item);
    commands.push(fn(viewModel));
  }
  return commands;
};

const render2 = (uid, diffResult) => {
  const {
    newState,
    oldState
  } = get$1(uid);
  if (oldState === newState) {
    return [];
  }
  set(uid, newState, newState);
  const commands = applyRender(oldState, newState, diffResult);
  return commands;
};

const renderEventListeners = () => {
  return [{
    name: HandlePointerDown,
    params: ['handleClickAt', 'event.clientX', 'event.clientY'],
    preventDefault: true
  }, {
    name: HandleWheel,
    params: ['handleWheel', 'event.deltaMode', 'event.deltaY'],
    passive: true
  }, {
    name: HandleBlur,
    params: ['handleBlur']
  }, {
    name: HandleBeforeInput,
    params: ['handleBeforeInput']
  }, {
    name: HandleInput,
    params: ['handleInput', 'event.target.value']
  }, {
    name: HandleFocus,
    params: ['handleFocus']
  }];
};

const selectCurrentIndex = state => {
  const {
    focusedIndex
  } = state;
  return selectIndex(state, focusedIndex);
};

const findLabelIndex = (items, label) => {
  for (let i = 0; i < items.length; i++) {
    if (items[i].label === label) {
      return i;
    }
  }
  return -1;
};

const selectItem = async (state, label) => {
  string(label);
  const index = findLabelIndex(state.items, label);
  if (index === -1) {
    return state;
  }
  return selectIndex(state, index);
};

const showQuickInput = async ({
  ignoreFocusOut,
  initialValue,
  waitUntil
}) => {
  // TODO ask renderer worker to create quickpick instance, with given options
  const picks = [];
  // const id=QuickPickCallbacks.registerCallback()
  await invoke$1('QuickPick.showCustom', picks, {
    ignoreFocusOut,
    initialValue,
    waitUntil
  });
  return {
    canceled: false,
    inputValue: ''
  };
};

const commandMap = {
  'QuickPick.addMenuEntries': add,
  'QuickPick.close': close,
  'QuickPick.create2': create$8,
  'QuickPick.diff2': diff2,
  'QuickPick.dispose': dispose,
  'QuickPick.executeCallback': executeCallback,
  'QuickPick.focusFirst': wrapCommand(focusFirst),
  'QuickPick.focusIndex': wrapCommand(focusIndex),
  'QuickPick.focusLast': wrapCommand(focusLast),
  'QuickPick.focusNext': wrapCommand(focusNext),
  'QuickPick.focusPrevious': wrapCommand(focusPrevious),
  'QuickPick.getCommandIds': getCommandIds,
  'QuickPick.getKeyBindings': getKeyBindings,
  'QuickPick.handleBeforeInput': wrapCommand(handleBeforeInput),
  'QuickPick.handleBlur': wrapCommand(handleBlur),
  'QuickPick.handleClickAt': wrapCommand(handleClickAt),
  'QuickPick.handleFocus': wrapCommand(handleFocus),
  'QuickPick.handleInput': wrapCommand(handleInput),
  'QuickPick.handleMessagePort': handleMessagePort,
  'QuickPick.handleWheel': wrapCommand(handleWheel),
  'QuickPick.initialize': initialize,
  'QuickPick.loadContent': wrapCommand(loadContent),
  'QuickPick.render2': render2,
  'QuickPick.renderEventListeners': renderEventListeners,
  'QuickPick.selectCurrentIndex': wrapCommand(selectCurrentIndex),
  'QuickPick.selectIndex': wrapCommand(selectIndex),
  'QuickPick.selectItem': wrapCommand(selectItem),
  'QuickPick.setDeltaY': wrapCommand(setDeltaY),
  'QuickPick.setValue': wrapCommand(setValue),
  'QuickPick.showQuickInput': showQuickInput
};

const initializeEditorWorker = async () => {
  const rpc = await create$1({
    commandMap: {},
    async send(port) {
      await sendMessagePortToEditorWorker(port, 0);
    }
  });
  set$2(rpc);
};

const initializeRendererWorker = async () => {
  const rpc = await create({
    commandMap: commandMap
  });
  set$1(rpc);
};

const Memfs = 'memfs';
const Html = 'html';
const Fetch = 'fetch';
const File = 'file';
const Default = '';

const searchFile$3 = async uri => {
  return invoke$1('ExtensionHost.searchFileWithMemory', uri);
};

// TODO simplify code
// 1. don't have playground prefix in fileMap json
// 2. remove code here that removes the prefix
const searchFile$2 = async path => {
  return invoke$1('ExtensionHost.searchFileWithFetch', path);
};

const searchFile$1 = async uri => {
  return invoke$1('ExtensionHost.searchFileWithHtml', uri);
};

const getFileSearchRipGrepArgs = () => {
  const ripGrepArgs = ['--files', '--sort-files', '--hidden', '--glob', '!.git'];
  return ripGrepArgs;
};

const invoke = (method, ...params) => {
  return invoke$1('SearchProcess.invoke', method, ...params);
};

// TODO create direct connection from electron to file search worker using message ports

const searchFile = async (path, value, prepare) => {
  const ripGrepArgs = getFileSearchRipGrepArgs();
  const options = {
    limit: 9_999_999,
    ripGrepArgs,
    searchPath: path
  };
  const stdout = await invoke('SearchFile.searchFile', options);
  const lines = splitLines$2(stdout);
  return lines;
};

const searchModules = {
  [Default]: searchFile,
  [Fetch]: searchFile$2,
  [File]: searchFile,
  [Html]: searchFile$1,
  [Memfs]: searchFile$3
};

const listen = async () => {
  Object.assign(commandMapRef, commandMap);
  registerCommands(commandMap);
  register(searchModules);
  await Promise.all([initializeRendererWorker(), initializeEditorWorker()]);
};

const main = async () => {
  await listen();
};

main();
