我正在构建一个通过codemirror突出显示语法的React App。
我希望能够检测到任何语法标记上的悬停。我发现一个JavaScript扩展似乎可以做到这一点,CodeMirror-extension
我的问题是我不了解如何在我的react应用程序中使用javascript扩展。回购中的演示是一个html文件,例如:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Token Hover</title>
<!-- CodeMirror -->
<link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
<script src="../node_modules/codemirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="../node_modules/codemirror/theme/eclipse.css">
<script src="../node_modules/codemirror/addon/selection/active-line.js"></script>
<script src="../node_modules/codemirror/mode/javascript/javascript.js"></script>
<!-- CodeMirror Extension -->
<link rel="stylesheet" href="../addon/hover/text-hover.css">
<script src="../addon/hover/text-hover.js"></script>
<script src="../addon/hover/token-hover.js"></script>
<style>
.CodeMirror-hover {
outline:1px solid grey;
}
</style>
</head>
<body>
<h1>Token Hover</h1>
<form><textarea id="code" name="code">(function() {
"use strict";
function showTooltip(e, content) {
var tt = document.createElement("div");
tt.className = "CodeMirror-hover-tooltip";
if (typeof content == "string") {
content = document.createTextNode(content);
}
tt.appendChild(content);
document.body.appendChild(tt);
function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
tt.style.left = (e.clientX + 5) + "px";
}
CodeMirror.on(document, "mousemove", position);
position(e);
if (tt.style.opacity != null) tt.style.opacity = 1;
return tt;
}
function rm(elt) {
if (elt.parentNode) elt.parentNode.removeChild(elt);
}
function hideTooltip(tt) {
if (!tt.parentNode) return;
if (tt.style.opacity == null) rm(tt);
tt.style.opacity = 0;
setTimeout(function() { rm(tt); }, 600);
}
function showTooltipFor(e, content, node, state) {
if (state.node != node) return;
var tooltip = showTooltip(e, content);
function hide() {
CodeMirror.off(node, "mouseout", hide);
CodeMirror.off(node, "click", hide);
node.className = node.className.substring(0, node.className.length
- ' CodeMirror-hover'.length);
if (tooltip) { hideTooltip(tooltip); if (state.node === node) state.node = node; tooltip = null; }
}
var poll = setInterval(function() {
if (tooltip) for (var n = node;; n = n.parentNode) {
if (n == document.body) return;
if (!n) { hide(); break; }
}
if (!tooltip) return clearInterval(poll);
}, 400);
CodeMirror.on(node, "mouseout", hide);
CodeMirror.on(node, "click", hide);
}
function TextHoverState(cm, options) {
this.options = options;
this.timeout = null;
this.onMouseOver = function(e) { onMouseOver(cm, e); };
}
function parseOptions(cm, options) {
if (options instanceof Function) return {getTextHover: options};
if (!options || options === true) options = {};
if (!options.getTextHover) options.getTextHover = cm.getHelper(CodeMirror.Pos(0, 0), "textHover");
if (!options.getTextHover) throw new Error("Required option 'getTextHover' missing (text-hover addon)");
return options;
}
function onMouseOver(cm, e) {
var node = e.target || e.srcElement;
if (node) {
var state = cm.state.textHover;
state.node = node;
var content = state.options.getTextHover(cm, node, e);
if (content) {
node.className += ' CodeMirror-hover'
//clearTimeout(state.timeout);
//state.timeout = setTimeout(function() {showTooltipFor(e, content, node, state);}, 300);
showTooltipFor(e, content, node, state);
}
}
}
function optionHandler(cm, val, old) {
if (old && old != CodeMirror.Init) {
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.textHover.onMouseOver);
delete cm.state.textHover;
}
if (val) {
var state = cm.state.textHover = new TextHoverState(cm, parseOptions(cm, val));
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
}
}
function isTokenType(type, typeToSearch) {
if (!type)
return false;
return type.indexOf(typeToSearch) != -1;
}
CodeMirror.isTokenType = isTokenType;
function findTokenAt(cm, pos, types) {
var type = cm.getTokenTypeAt(pos);
if (type) {
if (!types)
return cm.getTokenAt(pos);
for ( var j = 0; j < types.length; j++) {
if (isTokenType(type, types[j])) {
return cm.getTokenAt(pos);
}
}
}
}
// When the mouseover fires, the cursor might not actually be over
// the character itself yet. These pairs of x,y offsets are used to
// probe a few nearby points when no suitable marked range is found.
var nearby = [ 0, 0, 0, 5, 0, -5, 5, 0, -5, 0 ];
CodeMirror.defineExtension("findTokenAt", function(e, types) {
var cm = this;
for ( var i = 0; i < nearby.length; i += 2) {
var pos = cm.coordsChar({
left : e.clientX + nearby[i],
top : e.clientY + nearby[i + 1]
});
var token = findTokenAt(cm, pos, types);
if (token) return token;
}
});
CodeMirror.defineOption("textHover", false, optionHandler); // deprecated
})();
</textarea></form>
<script type="text/javascript">
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: 'application/javascript',
theme : "eclipse",
styleActiveLine: true,
lineNumbers: true,
gutters: ["CodeMirror-linenumbers"],
textHover: true
});
</script>
</body>
</html>
我通过npm i codemirror-extension
下载了它。我已经看到一些关于在componentDidMount
中加载脚本的SO帖子-但是您如何使用该脚本?
这是a sandbox of my project currently:
import React from "react";
import ReactDOM from "react-dom";
import { UnControlled as CodeMirror } from "react-codemirror2";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/material.css";
import "codemirror/mode/xml/xml";
import "codemirror/mode/javascript/javascript";
import "./styles.css";
function App() {
return (
<div className="App">
<CodeMirror
value="<h1>I ♥ react-codemirror2</h1>"
options={{
mode: "xml",
theme: "material",
lineNumbers: true
}}
onChange={(editor, data, value) => {}}
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);