lodash#escapeRegExp TypeScript Examples

The following examples show how to use lodash#escapeRegExp. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: order.ts    From Adachi-BOT with MIT License 6 votes vote down vote up
constructor( config: OrderConfig, botCfg: BotConfig ) {
		super( config );
		
		const globalHeader: string = botCfg.header;
		const headers: string[] = config.headers.map( el => Order.header( el, globalHeader ) );
		
		let rawRegs = <string[][]>config.regexps;
		const isDeep: boolean = config.regexps.some( el => el instanceof Array );
		if ( !isDeep ) {
			rawRegs = [ <string[]>config.regexps ];
		}
		
		for ( let header of headers ) {
			const pair: RegPair = { header, genRegExps: [] };
			for ( let reg of rawRegs ) {
				const r: string = [ "", ...reg ].join( " *" );
				const h: string = escapeRegExp( header );
				const pattern: string = Order.addStartStopChar(
					h + r,
					config.start !== false,
					config.stop !== false
				);
				pair.genRegExps.push( Order.regexp( pattern, this.ignoreCase ) );
			}
			this.regPairs.push( pair );
		}
	}
Example #2
Source File: GetTypeDeclarationsOfFn.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function GetTypeDeclarationsOfFn(
  name: string,
  functions: StoryboardFunction[]
): ExtraLib[] {
  const libs: ExtraLib[] = [];
  const exports: string[] = [];
  for (const fn of functions) {
    if (fn.name === name) {
      continue;
    }
    libs.push({
      filePath: `storyboard/functions/${fn.name}.${
        fn.typescript ? "ts" : "js"
      }`,
      content: fn.source.replace(
        new RegExp(`\\bfunction\\s+${escapeRegExp(fn.name)}\\b\\s*\\(`),
        "export $&"
      ),
      model: true,
    });
    exports.push(`export * from "./functions/${fn.name}";`);
  }
  libs.push(
    {
      filePath: "storyboard/FN-exports.ts",
      content: exports.join("\n"),
    },
    {
      filePath: "storyboard/FN.d.ts",
      content: 'declare const FN: typeof import("./FN-exports");',
    }
  );
  return libs;
}
Example #3
Source File: generateDoc.ts    From next-core with GNU General Public License v3.0 6 votes vote down vote up
matchEventDecorate = (eventType: string, fileText: string) => {
  const reg = new RegExp(
    "(?<=(\\/\\*\\*(.|@|\\s)*?\\*\\/\\s*))" +
      "@event\\(.*?(type:\\s*(\"|')" +
      escapeRegExp(eventType) +
      "(\"|')).*?\\)"
  );
  return fileText.match(reg);
}
Example #4
Source File: SearchResultItem.tsx    From querybook with Apache License 2.0 6 votes vote down vote up
HighlightTitle: React.FunctionComponent<{
    title: string;
    searchString: string;
}> = ({ title, searchString }) => {
    const highlightReplace = (text: string) => `<mark>${text}</mark>`;
    let highlightedTitle = title;
    if (searchString && searchString.length) {
        const searchStringRegex = new RegExp(escapeRegExp(searchString), 'ig');
        highlightedTitle = title.replace(searchStringRegex, highlightReplace);
    }

    return highlightedTitle && highlightedTitle !== 'Untitled' ? (
        <AccentText size="smedium" weight="bold" color="text" hover>
            <div
                className="result-item-title"
                dangerouslySetInnerHTML={{
                    __html: highlightedTitle,
                }}
            />
        </AccentText>
    ) : (
        <UntitledText size="smedium" />
    );
}
Example #5
Source File: logs.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
LogsParsers: { [name: string]: LogsParser } = {
  JSON: {
    buildMatcher: label => new RegExp(`(?:{|,)\\s*"${label}"\\s*:\\s*"?([\\d\\.]+|[^"]*)"?`),
    getFields: line => {
      try {
        const parsed = JSON.parse(line);
        return Object.keys(parsed).map(key => {
          return `"${key}":${JSON.stringify(parsed[key])}`;
        });
      } catch {}
      return [];
    },
    getLabelFromField: field => (field.match(/^"([^"]+)"\s*:/) || [])[1],
    getValueFromField: field => (field.match(/:\s*(.*)$/) || [])[1],
    test: line => {
      try {
        return JSON.parse(line);
      } catch (error) {}
    },
  },

  logfmt: {
    buildMatcher: label => new RegExp(`(?:^|\\s)${escapeRegExp(label)}=("[^"]*"|\\S+)`),
    getFields: line => {
      const fields: string[] = [];
      line.replace(new RegExp(LOGFMT_REGEXP, 'g'), substring => {
        fields.push(substring.trim());
        return '';
      });
      return fields;
    },
    getLabelFromField: field => (field.match(LOGFMT_REGEXP) || [])[1],
    getValueFromField: field => (field.match(LOGFMT_REGEXP) || [])[2],
    test: line => LOGFMT_REGEXP.test(line),
  },
}
Example #6
Source File: switch.ts    From Adachi-BOT with MIT License 5 votes vote down vote up
constructor( config: SwitchConfig, botCfg: BotConfig ) {
		super( config );
		
		if ( config.onKey === config.offKey ) {
			throw `指令 ${ config.cmdKey } 配置错误: onKey 与 offKey 不可相同`;
		}
		if ( config.offKey.length === 0 || config.onKey.length === 0 ) {
			throw `指令 ${ config.cmdKey } 配置错误: onKey 和 offKey 不可为空`;
		}
		
		const globalHeader: string = botCfg.header;
		const process: ( h: string ) => string = h => escapeRegExp(
			Switch.header( h, globalHeader )
		);
		const addChar: ( s: string ) => string = s => Switch.addStartStopChar(
			s, config.start !== false, config.stop !== false
		);
		
		this.mode = config.mode;
		this.header = "";
		
		if ( config.mode === "single" ) {
			let reg: string = config.regexp instanceof Array
							? [ "", ...config.regexp ].join( " *" )
							: config.regexp;
			const h: string = process( config.header );
			const r: string = reg.replace(
				"#{OPT}",
				`(${ config.onKey }|${ config.offKey })`
			);
			this.regexps.push( Switch.regexp( addChar( h + r ), this.ignoreCase ) );
			this.keys = [ config.onKey, config.offKey ];
			this.header = h;
		} else {
			const r: string = config.regexp instanceof Array
							? [ "", ...config.regexp.filter( el => el !== "#{OPT}" ) ].join( " *" )
							: config.regexp.replace( "#{OPT}", "" ).trim();
			const h1: string = process( config.onKey );
			const h2: string = process( config.offKey );
			this.regexps.push( Switch.regexp(
				addChar( `(${ h1 }|${ h2 })${ r }` ),
				this.ignoreCase
			) );
			this.keys = [ h1, h2 ];
		}
	}
Example #7
Source File: GetUnusedImages.ts    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export async function DeleteUnusedImages({
  appId,
  projectId,
  storyboard,
  bucketName,
}: DeleteUnUseImagesProps): Promise<{ unusedImages: Array<imageItem> }> {
  // 获取所有图片
  const allImageReq = InstanceApi_postSearch("MICRO_APP_RESOURCE_IMAGE", {
    fields: { url: true },
    page_size: 3000,
    query: { "project.instanceId": projectId },
  });
  const allDocListReq = DocumentApi_getDocumentsTreeByAppId(appId);

  const [allImageResponse, allDocResponse] = await Promise.all([
    allImageReq,
    allDocListReq,
  ]);

  if (allImageResponse.list.length === 0) {
    return {
      unusedImages: [],
    };
  }

  const regexString = `${escapeRegExp(
    `/next/api/gateway/object_store.object_store.GetObject/api/v1/objectStore/bucket/${bucketName}/object/`
  )}(.+?\\.(?:png|jpe?g|gif))`;
  const regexSingleMatch = new RegExp(regexString);
  const regexMultipleMatch = new RegExp(regexSingleMatch, "g");

  const allImagesMap = new Map<string, string>();
  allImageResponse.list.forEach((item) => {
    const matches = item.url.match(regexSingleMatch);
    if (!matches) {
      throw new Error(`Unexpected image url: ${item.url}`);
    }
    allImagesMap.set(matches[1], item.instanceId);
  });
  const getDocDetailReq = allDocResponse.documentsTree.map((item) =>
    DocumentApi_getDocumentsDetails(item.documentId)
  );
  const allDocContentList = (await Promise.all(getDocDetailReq)).map(
    (item) => item.content
  );

  const usedImages = new Set<string>();
  findUsedImagesInStoryboard(storyboard, regexMultipleMatch, usedImages);
  findUsedImagesInString(
    allDocContentList.join("\n"),
    regexMultipleMatch,
    usedImages
  );

  const unusedImages: Array<imageItem> = [];

  for (const [imageName, instanceId] of allImagesMap.entries()) {
    if (!usedImages.has(imageName)) {
      unusedImages.push({
        name: imageName,
        instanceId,
      });
    }
  }

  return {
    unusedImages,
  };
}
Example #8
Source File: generateDoc.ts    From next-core with GNU General Public License v3.0 5 votes vote down vote up
propertyDefinitionReg = (propertyName: string) => {
  return new RegExp(
    "\\|\\s*[\"']?" +
      escapeRegExp(propertyName) +
      "((\\s*[\"']?(?<!\\\\)\\|(((?<=\\\\)\\|)|[^|])*){4}\\|)"
  );
}
Example #9
Source File: generateDoc.ts    From next-core with GNU General Public License v3.0 5 votes vote down vote up
commentReg = (text: string) => {
  return new RegExp(
    "(?<=(\\/\\*\\*(.|@|\\s)*?\\*\\/\\s*))" + escapeRegExp(text)
  );
}
Example #10
Source File: generateDoc.ts    From next-core with GNU General Public License v3.0 5 votes vote down vote up
matchMethodDecorate = (methodName: string, fileText: string) => {
  const reg = new RegExp(
    "(?<=(\\/\\*\\*(.|@|\\s)*?\\*\\/\\s*))" +
      "@method\\([^)]*?\\)\\s*" +
      escapeRegExp(methodName)
  );
  return fileText.match(reg);
}
Example #11
Source File: lexical.ts    From next-core with GNU General Public License v3.0 5 votes vote down vote up
export function getRegExpOfPlaceholder(symbols: string | string[]): RegExp {
  return new RegExp(
    `(${([] as string[])
      .concat(symbols)
      .map((symbol) => escapeRegExp(symbol))
      .join("|")})\\{`
  );
}
Example #12
Source File: InputTypeahead.tsx    From hub with Apache License 2.0 4 votes vote down vote up
InputTypeahead = forwardRef((props: Props, ref: Ref<RefInputTypeaheadField>) => {
  const inputEl = useRef<HTMLInputElement>(null);
  const itemsWrapper = useRef<HTMLDivElement | null>(null);
  const [inputValue, setInputValue] = useState('');
  const [highlightedText, setHighlightedText] = useState<RegExp | null>(null);
  const [highlightedItem, setHighlightedItem] = useState<number | null>(null);

  useImperativeHandle(ref, () => ({
    reset: () => {
      setInputValue('');
    },
    getValue(): string {
      return inputValue;
    },
    updateValue(newValue: string): void {
      setInputValue(newValue);
    },
  }));

  const getVisibleItems = useCallback((): Option[] | null => {
    let filteredItems: Option[] = [];
    let elements: any[] | null = null;

    if (!isNull(highlightedItem)) {
      setHighlightedItem(null);
    }

    if (isUndefined(props.displayItemsInValueLength) || inputValue.length >= props.displayItemsInValueLength) {
      filteredItems = props.options.filter((opt: Option) => opt.name.toLowerCase().includes(inputValue.toLowerCase()));
      elements = orderBy(
        filteredItems,
        [
          (item: Option) =>
            props.selected.hasOwnProperty(item.filterKey) && props.selected[item.filterKey].includes(item.id.toString())
              ? -1
              : 1,
          'total',
        ],
        ['asc', 'desc']
      );
      // Scroll top when new visible items are displayed
      if (itemsWrapper && itemsWrapper.current) {
        itemsWrapper.current.scroll(0, 0);
      }
    }

    return elements;
  }, [highlightedItem, props.displayItemsInValueLength, props.options, props.selected, inputValue]);

  const getSelectedItems = useCallback((): Option[] => {
    let selectedItems: Option[] = [];
    Object.keys(props.selected).forEach((fKey: string) => {
      props.selected[fKey].forEach((item: string) => {
        const selected = props.options.find((opt: Option) => opt.id.toString() === item && opt.filterKey === fKey);
        if (!isUndefined(selected)) {
          selectedItems.push(selected);
        }
      });
    });
    return orderBy(selectedItems, 'total', 'desc');
  }, [props.options, props.selected]);

  const getOptionName = (name: string): JSX.Element => {
    if (!isNull(highlightedText)) {
      const stringParts: string[] = compact(name.split(highlightedText));
      if (stringParts.length === 1) {
        return (
          <span
            className={classnames({
              'fw-bold highlighted': name.toLowerCase() === inputValue.toLowerCase(),
            })}
          >
            {name}
          </span>
        );
      }

      return (
        <>
          {stringParts.map((str: string, index: number) => (
            <span
              key={`${name}_${index}`}
              className={classnames({
                'fw-bold highlighted': str.toLowerCase() === inputValue.toLowerCase(),
              })}
            >
              {str}
            </span>
          ))}
        </>
      );
    } else {
      return <>{name}</>;
    }
  };

  const [visibleItems, setVisibleItems] = useState<Option[] | null>(null);
  const [selectedItems, setSelectedItems] = useState<Option[]>(getSelectedItems());

  useEffect(() => {
    setVisibleItems(getVisibleItems());
  }, [inputValue, props.options]); /* eslint-disable-line react-hooks/exhaustive-deps */

  useEffect(() => {
    setSelectedItems(getSelectedItems());
  }, [getSelectedItems, props.selected]);

  const onChange = (e: ChangeEvent<HTMLInputElement>) => {
    e.stopPropagation();
    e.preventDefault();

    setHighlightedItem(null);
    setInputValue(e.target.value);

    const escapedValue = escapeRegExp(e.target.value.toLowerCase());
    setHighlightedText(e.target.value !== '' ? new RegExp(`(${escapedValue})`, 'gi') : null);
  };

  const onSelect = (filterKey: string, id: string, isSelected: boolean) => {
    setHighlightedItem(null);
    props.onChange(filterKey, id, !isSelected);
    if (props.onChangeSelection) {
      props.onChangeSelection();
    }
  };

  const onKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
    switch (e.key) {
      case 'ArrowDown':
        updateHighlightedItem('down');
        return;
      case 'ArrowUp':
        updateHighlightedItem('up');
        return;
      case 'Enter':
        if (!isNull(highlightedItem) && visibleItems) {
          const item = visibleItems[highlightedItem];
          const isSelected =
            props.selected.hasOwnProperty(item.filterKey) &&
            props.selected[item.filterKey].includes(item.id.toString());
          onSelect(item.filterKey, item.id.toString(), isSelected);
        }
        return;
      default:
        return;
    }
  };

  const scrollDropdown = (index: number) => {
    if (itemsWrapper && itemsWrapper.current) {
      const itemsOnScreen = Math.floor(itemsWrapper.current.clientHeight / ITEM_HEIGHT);
      if (index + 1 > itemsOnScreen) {
        itemsWrapper.current.scroll(0, (index + 1 - itemsOnScreen) * ITEM_HEIGHT);
      } else {
        itemsWrapper.current.scroll(0, 0);
      }
    }
  };

  const updateHighlightedItem = (arrow: 'up' | 'down') => {
    if (!isNull(highlightedItem)) {
      let newIndex: number = arrow === 'up' ? highlightedItem - 1 : highlightedItem + 1;
      if (newIndex > visibleItems!.length - 1) {
        newIndex = 0;
      }
      if (newIndex < 0) {
        newIndex = visibleItems!.length - 1;
      }
      scrollDropdown(newIndex);
      setHighlightedItem(newIndex);
    } else {
      if (visibleItems && visibleItems.length > 0) {
        const newIndex = arrow === 'up' ? visibleItems.length - 1 : 0;
        scrollDropdown(newIndex);
        setHighlightedItem(newIndex);
      }
    }
  };

  if (props.options.length === 0) return null;

  return (
    <>
      <div className={`mb-3 input-group-sm ${styles.inputWrapper} ${props.inputWrapperClassName}`}>
        <input
          ref={inputEl}
          type="text"
          placeholder={props.placeholder || `Search ${props.label}`}
          className={classnames(
            'flex-grow-1 form-control',
            styles.input,
            { 'ps-3 pe-4': props.searchIcon },
            { 'px-3': isUndefined(props.searchIcon) || !props.searchIcon }
          )}
          name={`inputTypeahead_${props.label}`}
          value={inputValue}
          onChange={onChange}
          onKeyDown={onKeyDown}
          spellCheck="false"
          autoFocus={!isUndefined(props.autofocus) && props.autofocus}
        />

        {props.searchIcon && <FaSearch className={`text-muted position-absolute ${styles.searchIcon}`} />}

        {!isUndefined(props.additionalInfo) && <div className="alert p-0 mt-3">{props.additionalInfo}</div>}
      </div>

      {selectedItems.length > 0 && props.visibleClear && (
        <div className="py-1 border-bottom">
          <button
            className="btn btn-sm w-100"
            onClick={() => {
              if (props.onClear) {
                props.onClear();
              }
            }}
            aria-label="Clear all"
          >
            <div className="d-flex flex-row align-items-center text-muted">
              <IoIosClose />
              <small className="ms-2">Clear all</small>
            </div>
          </button>
        </div>
      )}

      {visibleItems && (
        <>
          {visibleItems.length === 0 ? (
            <div className={`p-3 text-center ${props.listClassName}`}>
              <small className="text-muted">Sorry, no matches found</small>
            </div>
          ) : (
            <div className={`${styles.itemsList} ${props.listClassName}`} ref={itemsWrapper}>
              {visibleItems.map((opt: Option, index: number) => {
                const isSelected =
                  props.selected.hasOwnProperty(opt.filterKey) &&
                  props.selected[opt.filterKey].includes(opt.id.toString());
                const name = getOptionName(opt.name);

                return (
                  <button
                    key={`opt_${opt.filterKey}_${opt.id}`}
                    data-testid="typeaheadDropdownBtn"
                    className={classnames(
                      'dropdown-item',
                      styles.option,
                      props.optClassName,
                      {
                        [styles.selected]: isSelected,
                      },
                      {
                        [styles.highlighted]: index === highlightedItem,
                      }
                    )}
                    onClick={() => {
                      onSelect(opt.filterKey, opt.id.toString(), isSelected);
                      if (props.onChangeSelection) {
                        props.onChangeSelection();
                      }
                    }}
                    aria-label={`${isSelected ? 'Unselect' : 'Select'} option`}
                  >
                    <div className="d-flex flex-row align-items-center position-relative">
                      {isSelected && (
                        <div className={`position-absolute ${styles.checkMark}`}>
                          <IoIosCheckmark />
                        </div>
                      )}
                      <InputTypeaheadOptionItem opt={opt} name={name} iconClassName={styles.icon} />

                      {isSelected && (
                        <div className={`position-absolute ${styles.close}`}>
                          <IoIosClose />
                        </div>
                      )}
                    </div>
                  </button>
                );
              })}
            </div>
          )}
        </>
      )}
    </>
  );
})
Example #13
Source File: SearchRepositories.tsx    From hub with Apache License 2.0 4 votes vote down vote up
SearchRepositories = (props: Props) => {
  const inputEl = useRef<HTMLInputElement>(null);
  const dropdownRef = useRef(null);
  const itemsWrapper = useRef<HTMLDivElement | null>(null);
  const [isSearching, setIsSearching] = useState(false);
  const [repositories, setRepositories] = useState<Repository[] | null>(null);
  const [searchName, setSearchName] = useState<string>('');
  const [dropdownTimeout, setDropdownTimeout] = useState<NodeJS.Timeout | null>(null);
  const [highlightedItem, setHighlightedItem] = useState<number | null>(null);

  useOutsideClick([dropdownRef], !isNull(repositories), () => cleanSearch());

  async function searchRepositories() {
    try {
      setIsSearching(true);
      let query: SearchQuery = {
        name: searchName,
        limit: DEFAULT_LIMIT,
        offset: 0,
      };
      if (props.extraQueryParams) {
        query = { ...query, filters: props.extraQueryParams };
      }
      const data = await API.searchRepositories(query);
      setRepositories(data.items);
      setIsSearching(false);
    } catch (err: any) {
      if (err.kind !== ErrorKind.Unauthorized) {
        alertDispatcher.postAlert({
          type: 'danger',
          message: 'An error occurred searching repositories, please try again later.',
        });
      } else {
        props.onAuthError();
      }
      setRepositories(null);
      setIsSearching(false);
    }
  }

  const saveSelectedRepository = (item: Repository): void => {
    setRepositories(null);
    setSearchName('');
    inputEl.current!.value = '';
    props.onSelection(item);
    setHighlightedItem(null);
  };

  const onChange = (e: ChangeEvent<HTMLInputElement>) => {
    setSearchName(e.target.value);
    setHighlightedItem(null);
  };

  const checkIfRepoIsDisabled = (item: Repository): boolean => {
    let isDisabled = false;
    if (!isUndefined(props.disabledRepositories)) {
      isDisabled =
        (!isUndefined(props.disabledRepositories.ids) && props.disabledRepositories.ids.includes(item.repositoryId!)) ||
        (!isUndefined(props.disabledRepositories.users) &&
          !isNull(item.userAlias) &&
          !isUndefined(item.userAlias) &&
          props.disabledRepositories.users.includes(item.userAlias)) ||
        (!isUndefined(props.disabledRepositories.organizations) &&
          !isNull(item.organizationName) &&
          !isUndefined(item.organizationName) &&
          props.disabledRepositories.organizations.includes(item.organizationName));
    }
    return isDisabled;
  };

  const onKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
    switch (e.key) {
      case 'Escape':
        cleanSearch();
        return;
      case 'ArrowDown':
        updateHighlightedItem('down');
        return;
      case 'ArrowUp':
        updateHighlightedItem('up');
        return;
      case 'Enter':
        e.preventDefault();
        if (!isNull(repositories) && !isNull(highlightedItem)) {
          const selectedRepo = repositories[highlightedItem];
          if (selectedRepo && !checkIfRepoIsDisabled(selectedRepo)) {
            saveSelectedRepository(selectedRepo);
          }
        }
        return;
      default:
        return;
    }
  };

  const updateHighlightedItem = (arrow: 'up' | 'down') => {
    if (!isNull(repositories) && repositories.length > 0) {
      if (!isNull(highlightedItem)) {
        let newIndex: number = arrow === 'up' ? highlightedItem - 1 : highlightedItem + 1;
        if (newIndex > repositories.length - 1) {
          newIndex = 0;
        }
        if (newIndex < 0) {
          newIndex = repositories.length - 1;
        }
        scrollDropdown(newIndex);
        setHighlightedItem(newIndex);
      } else {
        const newIndex = arrow === 'up' ? repositories.length - 1 : 0;
        scrollDropdown(newIndex);
        setHighlightedItem(newIndex);
      }
    }
  };

  const forceFocus = (): void => {
    if (!isNull(inputEl) && !isNull(inputEl.current)) {
      inputEl.current.focus();
    }
  };

  const cleanTimeout = () => {
    if (!isNull(dropdownTimeout)) {
      clearTimeout(dropdownTimeout);
      setDropdownTimeout(null);
    }
  };

  const cleanSearch = () => {
    setRepositories(null);
    setSearchName('');
    setHighlightedItem(null);
  };

  const scrollDropdown = (index: number) => {
    if (itemsWrapper && itemsWrapper.current) {
      const itemsOnScreen = Math.floor(itemsWrapper.current.clientHeight / ITEM_HEIGHT) - 1;
      if (index + 1 > itemsOnScreen) {
        itemsWrapper.current.scroll(0, (index - itemsOnScreen) * ITEM_HEIGHT);
      } else {
        itemsWrapper.current.scroll(0, 0);
      }
    }
  };

  useEffect(() => {
    const isInputFocused = inputEl.current === document.activeElement;
    if (searchName.length >= MIN_CHARACTERS_SEARCH && isInputFocused) {
      cleanTimeout();
      setDropdownTimeout(
        setTimeout(() => {
          searchRepositories();
        }, SEARCH_DELAY)
      );
    } else {
      cleanSearch();
    }

    return () => {
      if (!isNull(dropdownTimeout)) {
        clearTimeout(dropdownTimeout);
      }
    };
  }, [searchName]); /* eslint-disable-line react-hooks/exhaustive-deps */

  return (
    <div className="position-relative">
      <div className="d-flex flex-row">
        <div
          className={`flex-grow-1 d-flex align-items-stretch overflow-hidden position-relative searchBar lh-base bg-white ${styles.inputWrapper}`}
        >
          <div
            data-testid="searchBarIcon"
            className={`d-flex align-items-center ${styles.iconWrapper}`}
            onClick={forceFocus}
          >
            <FiSearch />
          </div>

          <input
            ref={inputEl}
            type="text"
            className={`flex-grow-1 pe-4 ps-2 ps-md-0 border-0 shadow-none bg-transparent ${styles.input}`}
            name="searchRepositoriesInput"
            aria-label="Search repositories"
            autoComplete="new-input"
            onChange={onChange}
            onKeyDown={onKeyDown}
            spellCheck="false"
          />

          {isSearching && (
            <div className={`position-absolute text-secondary ${styles.loading}`}>
              <span data-testid="searchBarSpinning" className="spinner-border spinner-border-sm" />
            </div>
          )}
        </div>
      </div>

      {!isNull(repositories) && (
        <div ref={dropdownRef} className={`dropdown-menu w-100 p-0 shadow-sm show overflow-hidden ${styles.dropdown}`}>
          {repositories.length === 0 ? (
            <p className="m-3 text-center">
              We can't seem to find any repositories that match your search for{' '}
              <span className="fw-bold">{searchName}</span>
            </p>
          ) : (
            <div className={`overflow-scroll ${styles.tableWrapper}`} ref={itemsWrapper}>
              <table
                className={`table table-hover table-sm mb-0 text-break ${styles.table}`}
                role="grid"
                aria-labelledby={props.label}
              >
                <thead>
                  <tr>
                    <th scope="col" className={`${styles.fitCell} d-none d-sm-table-cell`}></th>
                    <th scope="col" className={styles.repoCell}>
                      Repository
                    </th>
                    {props.visibleUrl && (
                      <th scope="col" className="d-none d-md-table-cell">
                        Url
                      </th>
                    )}
                    <th scope="col">Publisher</th>
                  </tr>
                </thead>
                <tbody>
                  {repositories.map((item: Repository, index: number) => {
                    const isDisabled = checkIfRepoIsDisabled(item);

                    return (
                      <tr
                        data-testid="repoItem"
                        role="button"
                        className={classnames(
                          { [styles.clickableCell]: !isDisabled },
                          { [styles.disabledCell]: isDisabled },
                          { [styles.activeCell]: index === highlightedItem }
                        )}
                        onClick={() => {
                          if (!isDisabled) {
                            saveSelectedRepository(item);
                          }
                        }}
                        key={`repo_${item.name!}`}
                        onMouseOver={() => setHighlightedItem(index)}
                        onMouseOut={() => setHighlightedItem(null)}
                      >
                        <td className="align-middle text-center d-none d-sm-table-cell">
                          <div className="mx-2">
                            <RepositoryIcon kind={item.kind} className={`w-auto ${styles.icon}`} />
                          </div>
                        </td>
                        <td className="align-middle">
                          <div className={styles.truncateWrapper}>
                            <div className="text-truncate">
                              {searchName === '' ? (
                                <>{item.name}</>
                              ) : (
                                <>
                                  {regexifyString({
                                    pattern: new RegExp(escapeRegExp(searchName), 'gi'),
                                    decorator: (match: string, index: number) => {
                                      return (
                                        <span key={`match_${item.name}_${index}`} className="fw-bold highlighted">
                                          {match}
                                        </span>
                                      );
                                    },
                                    input: item.name,
                                  })}
                                </>
                              )}
                            </div>
                          </div>
                        </td>
                        {props.visibleUrl && (
                          <td className="align-middle d-none d-md-table-cell">
                            <div className={styles.truncateWrapper}>
                              <div className="text-truncate">
                                <small>{item.url}</small>
                              </div>
                            </div>
                          </td>
                        )}
                        <td className="align-middle">
                          <div className="text-dark d-flex flex-row align-items-center">
                            <span className={`me-1 ${styles.tinyIcon}`}>
                              {item.userAlias ? <FaUser /> : <MdBusiness />}
                            </span>
                            {item.userAlias || item.organizationDisplayName || item.organizationName}
                          </div>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}
    </div>
  );
}
Example #14
Source File: context.tsx    From remix-project with MIT License 4 votes vote down vote up
SearchProvider = ({
  children = [],
  reducer = SearchReducer,
  initialState = SearchingInitialState,
  plugin = undefined
} = {}) => {
  const [state, dispatch] = useReducer(reducer, initialState)
  const [files, setFiles] = useState([])
  const clearSearchingTimeout = useRef(null)
  const value = {
    state,
    setFind: (value: string) => {
      plugin.cancel('fileManager')
      dispatch({
        type: 'SET_FIND',
        payload: value
      })
    },
    setReplace: (value: string) => {
      dispatch({
        type: 'SET_REPLACE',
        payload: value
      })
    },
    setReplaceEnabled: (value: boolean) => {
      dispatch({
        type: 'SET_REPLACE_ENABLED',
        payload: value
      })
    },
    setInclude: (value: string) => {
      dispatch({
        type: 'SET_INCLUDE',
        payload: value
      })
    },
    setExclude(value: string) {
      dispatch({
        type: 'SET_EXCLUDE',
        payload: value
      })
    },
    setCaseSensitive(value: boolean) {
      dispatch({
        type: 'SET_CASE_SENSITIVE',
        payload: value
      })
    },
    setWholeWord(value: boolean) {
      dispatch({
        type: 'SET_WHOLE_WORD',
        payload: value
      })
    },
    setRegex(value: boolean) {
      dispatch({
        type: 'SET_REGEX',
        payload: value
      })
    },
    setSearchResults(value: SearchResult[]) {
      dispatch({
        type: 'SET_SEARCH_RESULTS',
        payload: value
      })
    },
    reloadFile: async (file: string) => {
      dispatch({
        type: 'RELOAD_FILE',
        payload: file
      })
    },
    toggleUseRegex: () => {
      dispatch({
        type: 'TOGGLE_USE_REGEX',
        payload: undefined
      })
    },
    toggleCaseSensitive: () => {
      dispatch({
        type: 'TOGGLE_CASE_SENSITIVE',
        payload: undefined
      })
    },
    toggleMatchWholeWord: () => {
      dispatch({
        type: 'TOGGLE_MATCH_WHOLE_WORD',
        payload: undefined
      })
    },
    setReplaceWithoutConfirmation: (value: boolean) => {
      dispatch({
        type: 'SET_REPLACE_WITHOUT_CONFIRMATION',
        payload: value
      })
    },
    disableForceReload: (file: string) => {
      dispatch({
        type: 'DISABLE_FORCE_RELOAD',
        payload: file
      })
    },
    setCurrentFile: (file: string) => {
      dispatch({
        type: 'SET_CURRENT_FILE',
        payload: file
      })
    },
    setCurrentWorkspace: (workspace: any) => {
      dispatch({
        type: 'SET_CURRENT_WORKSPACE',
        payload: workspace
      })
    },
    updateCount: (count: number, file: string) => {
      dispatch({
        type: 'UPDATE_COUNT',
        payload: { count, file }
      })
    },
    setSearching(file: string) {
      dispatch({
        type: 'SET_SEARCHING',
        payload: file
      })
    },

    startSearch: () => {
      dispatch({
        type: 'START_SEARCH',
        payload: undefined
      })
    },

    setRun(value: boolean) {
      dispatch({
        type: 'SET_RUN',
        payload: value
      })
    },

    findText: async (path: string) => {
      if (!plugin) return
      try {
        if (state.find.length < 1) return
        value.setSearching(path)
        const text = await plugin.call('fileManager', 'readFile', path)
        const result: SearchResultLine[] = findLinesInStringWithMatch(
          text,
          createRegExFromFind()
        )
        clearTimeout(clearSearchingTimeout.current)
        clearSearchingTimeout.current = setTimeout(() => value.setSearching(null), 500)
        return result
      } catch (e) {
        console.log(e)
        value.setSearching(null)
        // do nothing
      }
    },
    hightLightInPath: async (
      result: SearchResult,
      line: SearchResultLineLine
    ) => {
      await plugin.call('editor', 'discardHighlight')
      await plugin.call('editor', 'highlight', line.position, result.path)
      await plugin.call(
        'editor',
        'revealRange',
        line.position.start.line,
        line.position.start.column,
        line.position.end.line,
        line.position.end.column
      )
    },
    replaceText: async (result: SearchResult, line: SearchResultLineLine) => {
      try {
        await plugin.call('editor', 'discardHighlight')
        await plugin.call('editor', 'highlight', line.position, result.path)
        const content = await plugin.call(
          'fileManager',
          'readFile',
          result.path
        )
        const replaced = replaceTextInLine(content, line, state.replace)
        await plugin.call('fileManager', 'setFile', result.path, replaced)
        setUndoState(content, replaced, result.path)
      } catch (e) {
        throw new Error(e)
      }
    },
    replaceAllInFile: async (result: SearchResult) => {
      await plugin.call('editor', 'discardHighlight')
      const content = await plugin.call('fileManager', 'readFile', result.path)
      const replaced = replaceAllInFile(
        content,
        createRegExFromFind(),
        state.replace
      )
      await plugin.call('fileManager', 'setFile', result.path, replaced)
      await plugin.call('fileManager', 'open', result.path)
      setUndoState(content, replaced, result.path)
    },
    setUndoEnabled: (path: string, workspace: string, content: string) => {
      dispatch({
        type: 'SET_UNDO_ENABLED',
        payload: {
          path,
          workspace,
          content
        }
      })
    },
    undoReplace: async (buffer: undoBufferRecord) => {
      const content = await plugin.call('fileManager', 'readFile', buffer.path)
      if (buffer.newContent !== content) {
        throw new Error('Can not undo replace, file has been changed.')
      }
      await plugin.call(
        'fileManager',
        'setFile',
        buffer.path,
        buffer.oldContent
      )
      await plugin.call('fileManager', 'open', buffer.path)
    },
    clearUndo: () => {
      dispatch({
        type: 'CLEAR_UNDO',
        payload: undefined
      })
    },

    clearStats: () => {
      plugin.call('editor', 'discardHighlight')
      dispatch({
        type: 'CLEAR_STATS',
        payload: undefined
      })
    },

    cancelSearch: async (clearResults = true) => {
      plugin.cancel('fileManager')
      if (clearResults) value.clearStats()
      value.setRun(false)
    },

    setClipped: (value: boolean) => {
      dispatch({
        type: 'SET_CLIPPED',
        payload: value
      })
    }
  }

  const reloadStateForFile = async (file: string) => {
    await value.reloadFile(file)
  }

  useEffect(() => {
    plugin.on('filePanel', 'setWorkspace', async workspace => {
      value.setSearchResults(null)
      value.clearUndo()
      value.setCurrentWorkspace(workspace.name)
      setFiles(await getDirectory('/', plugin))
    })
    plugin.on('fileManager', 'fileSaved', async file => {
      await reloadStateForFile(file)
      await checkUndoState(file)
    })
    plugin.on('fileManager', 'rootFolderChanged', async file => {
      const workspace = await plugin.call('filePanel', 'getCurrentWorkspace')
      if (workspace) value.setCurrentWorkspace(workspace.name)
      setFiles(await getDirectory('/', plugin))
    })

    plugin.on('fileManager', 'fileAdded', async file => {
      setFiles(await getDirectory('/', plugin))
      await reloadStateForFile(file)
    })
    plugin.on('fileManager', 'currentFileChanged', async file => {
      value.setCurrentFile(file)
      await checkUndoState(file)
    })
    async function fetchWorkspace() {
      try {
        const workspace = await plugin.call('filePanel', 'getCurrentWorkspace')
        if (workspace) value.setCurrentWorkspace(workspace.name)
        setFiles(await getDirectory('/', plugin))
      } catch (e) {
        console.log(e)
      }
    }

    fetchWorkspace()

    return () => {
      plugin.off('fileManager', 'fileChanged')
      plugin.off('filePanel', 'setWorkspace')
    }
  }, [])

  //*.sol, **/*.txt, contracts/*
  const setGlobalExpression = (paths: string) => {
    const results = []
    paths.split(',').forEach(path => {
      path = path.trim()
      if (path.startsWith('*.')) path = path.replace(/(\*\.)/g, '**/*.')
      if (path.endsWith('/*') && !path.endsWith('/**/*'))
        path = path.replace(/(\*)/g, '**/*.*')
      results.push(path)
    })
    return results
  }

  const checkUndoState = async (path: string) => {
    if (!plugin) return
    try {
      const content = await plugin.call('fileManager', 'readFile', path)
      const workspace = await plugin.call('filePanel', 'getCurrentWorkspace')
      value.setUndoEnabled(path, workspace.name, content)
    } catch (e) {
      console.log(e)
    }
  }

  const setUndoState = async (
    oldContent: string,
    newContent: string,
    path: string
  ) => {
    const workspace = await plugin.call('filePanel', 'getCurrentWorkspace')
    const undo = {
      oldContent,
      newContent,
      path,
      workspace: workspace.name
    }
    dispatch({
      type: 'SET_UNDO',
      payload: undo
    })
  }

  const createRegExFromFind = () => {
    let flags = 'g'
    let find = state.find
    if (!state.casesensitive) flags += 'i'
    if (!state.useRegExp) find = escapeRegExp(find)
    if (state.matchWord) find = `\\b${find}\\b`
    const re = new RegExp(find, flags)
    return re
  }

  useEffect(() => {
    if (state.count > 500) {
      value.setClipped(true)
      value.cancelSearch(false)
    }
  }, [state.count])

  useEffect(() => {
    if (state.find) {
      (async () => {
        try {
          const pathFilter: any = {}
          if (state.include) {
            pathFilter.include = setGlobalExpression(state.include)
          }
          if (state.exclude) {
            pathFilter.exclude = setGlobalExpression(state.exclude)
          }
          const filteredFiles = files
            .filter(filePathFilter(pathFilter))
            .map(file => {
              const r: SearchResult = {
                filename: file,
                lines: [],
                path: file,
                timeStamp: Date.now(),
                forceReload: false,
                count: 0
              }
              return r
            })
          value.setSearchResults(filteredFiles)
        } catch (e) {
          console.log(e)
        }
      })()
    }
  }, [state.timeStamp])

  return (
    <>
      <SearchContext.Provider value={value}>{children}</SearchContext.Provider>
    </>
  )
}