-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Supports searching webview content and quickly opening document…
…s from code (#144) * fix: styles * feat: 文档增强-可通过hover代码中的API打开API文档 * fix: 优化敏感信息 Linter * feat: 支持搜索并高亮定位webview中的内容 * test: 新增alicloud.api.quickOpenDocument命令测试 * test: 新增alicloud.api.quickOpenDocument命令测试
- Loading branch information
Showing
11 changed files
with
338 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,23 +17,25 @@ | |
"@monaco-editor/react": "^4.6.0", | ||
"@vercel/ncc": "^0.38.1", | ||
"@vscode-elements/elements": "^1.3.0", | ||
"ahooks": "3.8.1", | ||
"antd": "^5.12.3", | ||
"intl-format": "^1.2.0", | ||
"load-script": "^2.0.0", | ||
"lodash": "^4.17.20", | ||
"next": "^14.2.6", | ||
"pontx-semix-table": "0.5.3", | ||
"pontx-ui": "latest", | ||
"react": "^18.3.1", | ||
"react-copy-to-clipboard": "^5.1.0", | ||
"react-dom": "^18.3.1", | ||
"react-use-observer": "^2.2.4", | ||
"sass": "^1.77.8", | ||
"semix-schema-table": "^0.1.3", | ||
"semix-schema-table": "0.1.3", | ||
"styled-components": "^6.1.1", | ||
"xml2js": "^0.6.2" | ||
}, | ||
"resolutions": { | ||
"semix-schema-table": "^0.1.3" | ||
"semix-schema-table": "0.1.3" | ||
}, | ||
"version": "0.3.70", | ||
"repository": "[email protected]:aliyun/alibabacloud-api-vscode-toolkit.git", | ||
|
@@ -47,7 +49,6 @@ | |
"@types/vscode": "^1.91.0", | ||
"@types/vscode-webview": "^1.57.5", | ||
"@vitejs/plugin-react": "^1.3.2", | ||
"ahooks": "^3.8.1", | ||
"autoprefixer": "^10.0.1", | ||
"eslint": "^8", | ||
"eslint-config-next": "14.2.5", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/** | ||
* @author yini-chen | ||
* @description | ||
*/ | ||
import { CloseOutlined, DownOutlined, UpOutlined } from "@ant-design/icons"; | ||
import { Button, Input } from "antd"; | ||
import * as React from "react"; | ||
|
||
export class SearcherProps { | ||
contentRef: any; | ||
isVisible: boolean; | ||
setIsVisible: (visible: boolean) => void; | ||
mode?: string; | ||
} | ||
|
||
export const Searcher: React.FC<SearcherProps> = (props) => { | ||
const [searchTerm, setSearchTerm] = React.useState(""); | ||
const { contentRef } = props; | ||
const contentElement = contentRef.current; | ||
|
||
// 清除高亮 | ||
const clearHighlights = () => { | ||
setHighlightDoms([]); | ||
setCurrentIndex(0); | ||
const highlightedElements = document.querySelectorAll('span[style*="background-color: yellow"]'); | ||
highlightedElements.forEach((element) => { | ||
const parent = element.parentNode; | ||
if (parent) { | ||
const textNode = document.createTextNode(element.textContent || ""); | ||
parent.insertBefore(textNode, element); | ||
parent.removeChild(element); | ||
} | ||
}); | ||
}; | ||
|
||
const [highlightDoms, setHighlightDoms] = React.useState([]); | ||
|
||
React.useEffect(() => { | ||
// 清除之前的高亮 | ||
clearHighlights(); | ||
}, [searchTerm, props.mode]); | ||
|
||
const handleSearch = () => { | ||
const matchDoms = []; | ||
function highlightSampleText(searchTerm) { | ||
// 遍历所有节点的递归函数 | ||
function traverseNodes(node: Node) { | ||
// 如果是文本节点 | ||
if (node.nodeType === Node.TEXT_NODE) { | ||
const textContent = node.textContent || ""; | ||
const index = textContent?.toLocaleLowerCase()?.indexOf(searchTerm?.toLocaleLowerCase()); | ||
|
||
if (index !== -1) { | ||
const span = document.createElement("span"); | ||
span.style.backgroundColor = "yellow"; | ||
span.style.color = "#59636E"; | ||
span.textContent = textContent.substring(index, index + searchTerm.length); // 高亮的文本 | ||
span.id = "highlighted-text"; | ||
matchDoms.push(span); | ||
|
||
// 创建一个新的文本节点,包含高亮部分和其他部分 | ||
const beforeText = document.createTextNode(textContent.substring(0, index)); | ||
const afterText = document.createTextNode(textContent.substring(index + searchTerm.length)); | ||
|
||
// 替换当前的文本节点 | ||
const parent = node.parentNode; | ||
if (parent) { | ||
parent.insertBefore(beforeText, node); | ||
parent.insertBefore(span, node); | ||
parent.insertBefore(afterText, node); | ||
parent.removeChild(node); | ||
} | ||
} | ||
} else { | ||
// 除文本节点外,递归遍历子节点 | ||
node.childNodes.forEach(traverseNodes); | ||
} | ||
} | ||
|
||
// 从文档体开始遍历 | ||
traverseNodes(contentElement); | ||
setHighlightDoms(matchDoms); | ||
setCurrentIndex(matchDoms?.length ? 1 : 0); | ||
} | ||
if (searchTerm?.length) { | ||
if (highlightDoms?.length) { | ||
// 回车时移动到下一个高亮元素 | ||
if (highlightDoms[curIndex]) { | ||
setCurrentIndex(curIndex + 1); | ||
} else { | ||
setCurrentIndex(1); | ||
} | ||
} else { | ||
highlightSampleText(searchTerm); | ||
} | ||
} | ||
}; | ||
|
||
const [curIndex, setCurrentIndex] = React.useState(highlightDoms?.length ? 1 : 0); | ||
|
||
// 滚动到当前高亮元素 | ||
React.useEffect(() => { | ||
if (highlightDoms[curIndex - 1]) { | ||
highlightDoms[curIndex - 1].scrollIntoView({ behavior: "smooth", block: "center" }); | ||
} | ||
}, [highlightDoms, curIndex]); | ||
|
||
if (!props.isVisible) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<div className="searcher-wrapper fixed right-[24px] top-[74px] z-50 flex rounded-sm bg-[var(--vscode-badge-background)] p-1 shadow-sm shadow-[var(--vscode-badge-background)]"> | ||
<Input | ||
style={{ width: "200px" }} | ||
value={searchTerm} | ||
onChange={(e) => setSearchTerm(e.target.value)} | ||
onPressEnter={handleSearch} | ||
placeholder="Search..." | ||
size="small" | ||
className="h-8" | ||
id="page-search-input" | ||
/> | ||
<span className="m-auto text-nowrap p-2"> | ||
{curIndex}/{highlightDoms.length} | ||
</span> | ||
<div> | ||
<Button | ||
type="text" | ||
className="h-8 w-8 p-1" | ||
onClick={() => { | ||
if (curIndex !== 1) { | ||
setCurrentIndex(curIndex - 1); | ||
} else { | ||
setCurrentIndex(highlightDoms.length); | ||
} | ||
}} | ||
> | ||
<UpOutlined style={{ color: "var(--vscode-editor-foreground)", verticalAlign: "middle" }} /> | ||
</Button> | ||
<Button | ||
type="text" | ||
className="h-8 w-8 p-1" | ||
onClick={() => { | ||
if (highlightDoms[curIndex]) { | ||
setCurrentIndex(curIndex + 1); | ||
} else { | ||
setCurrentIndex(1); | ||
} | ||
}} | ||
> | ||
<DownOutlined style={{ color: "var(--vscode-editor-foreground)", verticalAlign: "middle" }} /> | ||
</Button> | ||
<Button | ||
type="text" | ||
className="h-8 w-8 p-1" | ||
onClick={() => { | ||
clearHighlights(); | ||
props.setIsVisible(false); | ||
}} | ||
> | ||
<CloseOutlined style={{ color: "var(--vscode-editor-foreground)", verticalAlign: "middle" }} /> | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
Searcher.defaultProps = new SearcherProps(); | ||
export default Searcher; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.