diff --git a/NOTICE.md b/NOTICE.md index ee0f75d15c..41e8ed91c4 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -52,7 +52,6 @@ Code copied from project eclipse-theia/theia - Source: - https://github.com/opensumi/core/blob/main/packages/core-browser/src/keyboard/keyboard-layout-service.ts - https://github.com/opensumi/core/blob/main/packages/core-browser/src/keyboard/keys.ts - - https://github.com/opensumi/core/blob/main/packages/core-browser/src/utils/parse.ts - https://github.com/opensumi/core/blob/main/packages/core-common/src/problem-matcher.ts - https://github.com/opensumi/core/blob/main/packages/core-common/src/problem-pattern.ts - https://github.com/opensumi/core/blob/main/packages/core-common/src/keyboard/keyboard-layout-provider.ts diff --git a/packages/core-browser/__tests__/utils/parse.test.ts b/packages/core-browser/__tests__/utils/parse.test.ts deleted file mode 100644 index b6ee612447..0000000000 --- a/packages/core-browser/__tests__/utils/parse.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { LabelIcon, parseLabel } from '../../src/utils/parse'; - -describe('it can parse', () => { - it('can parse label with icon', () => { - const labels = parseLabel('$(hello) world'); - expect(labels).toHaveLength(2); - expect((labels[0] as LabelIcon).name).toEqual('hello'); - }); - it('can parse label with multiple icon', () => { - const labels = parseLabel('$(hello) world $(qwq)'); - expect(labels).toHaveLength(3); - expect((labels[0] as LabelIcon).name).toEqual('hello'); - expect((labels[2] as LabelIcon).name).toEqual('qwq'); - }); - - it('can parse label with icon animation', () => { - const labels = parseLabel('$(hello~spin) world $(qwq)'); - expect(labels).toHaveLength(3); - expect((labels[0] as LabelIcon).name).toEqual('hello'); - expect((labels[0] as LabelIcon).owner).toBeUndefined(); - expect((labels[0] as LabelIcon).animation).toEqual('spin'); - expect((labels[2] as LabelIcon).name).toEqual('qwq'); - expect((labels[2] as LabelIcon).owner).toBeUndefined(); - }); - it('can parse label with icon owner', () => { - const labels = parseLabel('$(kt/hello~spin) world $(qwq)'); - expect(labels).toHaveLength(3); - expect((labels[0] as LabelIcon).name).toEqual('hello'); - expect((labels[0] as LabelIcon).animation).toEqual('spin'); - expect((labels[0] as LabelIcon).owner).toEqual('kt'); - expect((labels[2] as LabelIcon).name).toEqual('qwq'); - expect((labels[2] as LabelIcon).owner).toBeUndefined(); - }); -}); diff --git a/packages/core-browser/src/utils/index.ts b/packages/core-browser/src/utils/index.ts index 38522f32a6..606c2f53f3 100644 --- a/packages/core-browser/src/utils/index.ts +++ b/packages/core-browser/src/utils/index.ts @@ -2,6 +2,5 @@ export * from './env'; export * from './electron'; export * from './react-hooks'; export * from './icon'; -export * from './parse'; export * from './json'; export * from './label'; diff --git a/packages/core-browser/src/utils/label.tsx b/packages/core-browser/src/utils/label.tsx index f0aeb597e6..f90e3311e7 100644 --- a/packages/core-browser/src/utils/label.tsx +++ b/packages/core-browser/src/utils/label.tsx @@ -1,3 +1,4 @@ +import clx from 'classnames'; import React, { CSSProperties } from 'react'; import { Icon } from '@opensumi/ide-components/lib/icon/icon'; @@ -7,30 +8,42 @@ const SEPERATOR = ' '; export function transformLabelWithCodicon( label: string, - iconStyles: CSSProperties = {}, + iconStyleProps: CSSProperties | string = {}, transformer?: (str: string) => string | undefined, renderText?: (str: string, index: number) => React.ReactNode, ) { const ICON_REGX = /\$\(([a-z.]+\/)?([a-z-]+)(~[a-z]+)?\)/gi; const ICON_WITH_ANIMATE_REGX = /\$\(([a-z.]+\/)?([a-z-]+)~([a-z]+)\)/gi; + // some string like $() $(~spin) + const ICON_ERROR_REGX = /\$\(([a-z.]+\/)?([a-z-]+)?(~[a-z]+)?\)/gi; + + const generateIconStyle = (icon?: string, styleProps?: CSSProperties | string) => + typeof styleProps === 'string' ? { className: clx(icon, styleProps) } : { className: icon, style: styleProps }; + return label.split(SEPERATOR).map((e, index) => { if (!transformer) { return e; } const icon = transformer(e); if (icon) { - return ; + return ; } else if (ICON_REGX.test(e)) { if (e.includes('~')) { const [, , icon, animate] = ICON_WITH_ANIMATE_REGX.exec(e) || []; if (animate && icon) { return ( - + ); } } const newStr = e.replaceAll(ICON_REGX, (e) => `${SEPERATOR}${e}${SEPERATOR}`); - return transformLabelWithCodicon(newStr, iconStyles, transformer); + return transformLabelWithCodicon(newStr, iconStyleProps, transformer); + } else if (ICON_ERROR_REGX.test(e)) { + return transformLabelWithCodicon(e.replaceAll(ICON_ERROR_REGX, ''), iconStyleProps, transformer, renderText); } else { return isFunction(renderText) ? renderText(e, index) : {e}; } diff --git a/packages/core-browser/src/utils/parse.ts b/packages/core-browser/src/utils/parse.ts deleted file mode 100644 index 879de8fb70..0000000000 --- a/packages/core-browser/src/utils/parse.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** ****************************************************************************** - * Copyright (C) 2018 Red Hat, Inc. and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - ********************************************************************************/ -// Some code copied and modified from https://github.com/eclipse-theia/theia/tree/v1.14.0/packages/core/src/browser/label-parser.ts - -export interface LabelIcon { - name: string; - owner?: string; - animation?: string; -} - -export namespace LabelIcon { - export function is(val: object): val is LabelIcon { - return 'name' in val; - } -} - -export type LabelPart = string | LabelIcon; - -export function parseLabel(text: string): LabelPart[] { - const parserArray: LabelPart[] = []; - let arrPointer = 0; - let potentialIcon = ''; - - for (let idx = 0; idx < text.length; idx++) { - const char = text.charAt(idx); - parserArray[arrPointer] = parserArray[arrPointer] || ''; - if (potentialIcon === '') { - if (char === '$') { - potentialIcon += char; - } else { - parserArray[arrPointer] += char; - } - } else if (potentialIcon === '$') { - if (char === '(') { - potentialIcon += char; - } else { - parserArray[arrPointer] += potentialIcon + char; - potentialIcon = ''; - } - } else { - if (char === ')') { - const iconClassArr = potentialIcon.substring(2, potentialIcon.length).split('~'); - let name = iconClassArr[0]; - let owner: string | undefined; - if (name) { - const index = name.indexOf('/'); - if (index !== -1) { - owner = name.substring(0, index); - name = name.substring(index + 1); - } - } - if (parserArray[arrPointer] !== '') { - arrPointer++; - } - parserArray[arrPointer] = { name, owner, animation: iconClassArr[1] }; - arrPointer++; - potentialIcon = ''; - } else { - potentialIcon += char; - } - } - } - - if (potentialIcon !== '') { - parserArray[arrPointer] += potentialIcon; - } - - return parserArray; -} diff --git a/packages/quick-open/src/browser/components/highlight-label/index.tsx b/packages/quick-open/src/browser/components/highlight-label/index.tsx index d071c72d94..b89ed4081b 100644 --- a/packages/quick-open/src/browser/components/highlight-label/index.tsx +++ b/packages/quick-open/src/browser/components/highlight-label/index.tsx @@ -1,18 +1,9 @@ -import React from 'react'; +import React, { ReactChild } from 'react'; -import { getExternalIcon, LabelIcon, LabelPart, parseLabel } from '@opensumi/ide-core-browser'; +import { transformLabelWithCodicon } from '@opensumi/ide-core-browser'; import { Highlight } from '@opensumi/ide-core-browser/lib/quick-open'; -import { strings } from '@opensumi/ide-core-common'; - -const { escape } = strings; - -const labelWithIcons = (str: string, iconClassName?: string) => - parseLabel(escape(str)).reduce((pre: string | LabelPart, cur: LabelPart) => { - if (!(typeof cur === 'string') && LabelIcon.is(cur)) { - return pre + ``; - } - return pre + cur; - }, ''); +import { useInjectable } from '@opensumi/ide-core-browser/lib/react-hooks'; +import { IIconService } from '@opensumi/ide-theme'; export interface HighlightLabelProp { text?: string; @@ -33,8 +24,10 @@ export const HighlightLabel: React.FC = ({ hightLightClassName = '', OutElementType = 'span', }) => { + const iconService = useInjectable(IIconService); + const renderLabel = React.useMemo(() => { - const children: string[] = []; + const children: ReactChild[] = []; let pos = 0; for (const highlight of highlights) { @@ -43,26 +36,36 @@ export const HighlightLabel: React.FC = ({ } if (pos < highlight.start) { const substring = text.substring(pos, highlight.start); - children.push(`${labelWithIcons(substring, labelIconClassName)}`); + children.push( + + {transformLabelWithCodicon(substring, labelIconClassName, iconService.fromString.bind(iconService))} + , + ); pos = highlight.end; } const substring = text.substring(highlight.start, highlight.end); - children.push(`${labelWithIcons(substring, labelIconClassName)}`); + children.push( + + {transformLabelWithCodicon(substring, labelIconClassName, iconService.fromString.bind(iconService))} + , + ); pos = highlight.end; } if (pos < text.length) { const substring = text.substring(pos); - children.push(`${labelWithIcons(substring, labelIconClassName)}`); + children.push( + + {transformLabelWithCodicon(substring, labelIconClassName, iconService.fromString.bind(iconService))} + , + ); } - return children.join(''); + return children; }, [text, highlights]); return ( - + // @ts-ignore + + {renderLabel} + ); }; diff --git a/packages/quick-open/src/browser/quick-open.view.tsx b/packages/quick-open/src/browser/quick-open.view.tsx index df2b162bde..6ca80ab9df 100644 --- a/packages/quick-open/src/browser/quick-open.view.tsx +++ b/packages/quick-open/src/browser/quick-open.view.tsx @@ -257,6 +257,8 @@ const QuickOpenItemView: React.FC = observer(({ data, index {iconClass && } = observer(({ data, index = observer(({ data, index OutElementType='div' className={styles.item_label_detail} labelClassName={clx(styles.label_icon_container, styles.item_label_description_label)} - labelIconClassName={styles.label_has_icon} + labelIconClassName={clx(styles.label_has_icon, styles.item_label_detail_icon)} hightLightClassName={clx(styles.item_label_description_highlight)} text={detail} highlights={detailHighlights} diff --git a/packages/quick-open/src/browser/styles.module.less b/packages/quick-open/src/browser/styles.module.less index bc96f27c59..1a245d16d6 100644 --- a/packages/quick-open/src/browser/styles.module.less +++ b/packages/quick-open/src/browser/styles.module.less @@ -117,6 +117,10 @@ white-space: pre; } + .item_label_name_icon { + font-size: 14px; + } + .item_label_description_label { color: var(--descriptionForeground); } @@ -134,7 +138,9 @@ } .item_label_description, - .item_label_detail { + .item_label_detail, + .itam_label_description_icon, + .item_label_detail_icon { font-size: 12px; }