mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-22 16:03:39 +01:00
feat: environment variable syntax highlighting
This commit is contained in:
parent
d3d1e47950
commit
d165a04377
@ -24,7 +24,18 @@ const StyledWrapper = styled.div`
|
||||
font-family: Inter, sans-serif !important;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.cm-variable-valid{color: green}
|
||||
|
@ -24,6 +24,9 @@ class SingleLineEditor extends Component {
|
||||
lineNumbers: false,
|
||||
autofocus: true,
|
||||
mode: "brunovariables",
|
||||
brunoVarInfo: {
|
||||
variables: getEnvironmentVariables(this.props.collection),
|
||||
},
|
||||
extraKeys: {
|
||||
"Enter": () => {
|
||||
if (this.props.onRun) {
|
||||
@ -65,8 +68,7 @@ class SingleLineEditor extends Component {
|
||||
'Tab': () => {}
|
||||
},
|
||||
});
|
||||
this.editor.setValue(this.props.value)
|
||||
|
||||
this.editor.setValue(this.props.value);
|
||||
this.editor.on('change', (cm) => {
|
||||
this.props.onChange(cm.getValue());
|
||||
});
|
||||
@ -76,6 +78,7 @@ class SingleLineEditor extends Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
let variables = getEnvironmentVariables(this.props.collection);
|
||||
if (!isEqual(variables, this.variables)) {
|
||||
this.editor.options.brunoVarInfo.variables = variables;
|
||||
this.addOverlay();
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ if (!SERVER_RENDERED) {
|
||||
require('codemirror-graphql/info');
|
||||
require('codemirror-graphql/jump');
|
||||
require('codemirror-graphql/mode');
|
||||
|
||||
require('utils/codemirror/brunoVarInfo');
|
||||
}
|
||||
|
||||
export default function Main() {
|
||||
|
@ -12,6 +12,7 @@ import '../styles/globals.css';
|
||||
import 'tailwindcss/dist/tailwind.min.css';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'graphiql/graphiql.min.css';
|
||||
import 'utils/codemirror/brunoVarInfo.css';
|
||||
|
||||
function SafeHydrate({ children }) {
|
||||
return <div suppressHydrationWarning>{typeof window === 'undefined' ? null : children}</div>;
|
||||
|
28
packages/bruno-app/src/utils/codemirror/brunoVarInfo.css
Normal file
28
packages/bruno-app/src/utils/codemirror/brunoVarInfo.css
Normal file
@ -0,0 +1,28 @@
|
||||
.CodeMirror-brunoVarInfo {
|
||||
background: white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
||||
box-sizing: border-box;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
margin: 8px -8px;
|
||||
max-width: 800px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
padding: 8px 8px;
|
||||
position: fixed;
|
||||
transition: opacity 0.15s;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.CodeMirror-brunoVarInfo :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-brunoVarInfo :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-brunoVarInfo p {
|
||||
margin: 1em 0;
|
||||
}
|
198
packages/bruno-app/src/utils/codemirror/brunoVarInfo.js
Normal file
198
packages/bruno-app/src/utils/codemirror/brunoVarInfo.js
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Copyright (c) 2017, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file at https://github.com/graphql/codemirror-graphql/tree/v0.8.3
|
||||
*/
|
||||
|
||||
let CodeMirror;
|
||||
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||
|
||||
if (!SERVER_RENDERED) {
|
||||
CodeMirror = require('codemirror');
|
||||
|
||||
const renderVarInfo = (token, options, cm, pos) => {
|
||||
const str = token.string || '';
|
||||
|
||||
// str is of format {{variableName}}, extract variableName
|
||||
const variableName = str.substring(2, str.length - 2);
|
||||
|
||||
// get the value of the variable
|
||||
const variableValue = options.variables[variableName] || '';
|
||||
|
||||
const into = document.createElement('div');
|
||||
const descriptionDiv = document.createElement('div');
|
||||
descriptionDiv.className = 'info-description';
|
||||
|
||||
descriptionDiv.appendChild(document.createTextNode(variableValue));
|
||||
into.appendChild(descriptionDiv);
|
||||
|
||||
return into;
|
||||
};
|
||||
|
||||
CodeMirror.defineOption('brunoVarInfo', false, function(cm, options, old) {
|
||||
|
||||
if (old && old !== CodeMirror.Init) {
|
||||
const oldOnMouseOver = cm.state.brunoVarInfo.onMouseOver;
|
||||
CodeMirror.off(cm.getWrapperElement(), 'mouseover', oldOnMouseOver);
|
||||
clearTimeout(cm.state.brunoVarInfo.hoverTimeout);
|
||||
delete cm.state.brunoVarInfo;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
const state = (cm.state.brunoVarInfo = createState(options));
|
||||
state.onMouseOver = onMouseOver.bind(null, cm);
|
||||
CodeMirror.on(cm.getWrapperElement(), 'mouseover', state.onMouseOver);
|
||||
}
|
||||
});
|
||||
|
||||
function createState(options) {
|
||||
return {
|
||||
options:
|
||||
options instanceof Function
|
||||
? {render: options}
|
||||
: options === true ? {} : options,
|
||||
};
|
||||
}
|
||||
|
||||
function getHoverTime(cm) {
|
||||
const options = cm.state.brunoVarInfo.options;
|
||||
return (options && options.hoverTime) || 50;
|
||||
}
|
||||
|
||||
function onMouseOver(cm, e) {
|
||||
const state = cm.state.brunoVarInfo;
|
||||
const target = e.target || e.srcElement;
|
||||
|
||||
if (target.nodeName !== 'SPAN' || state.hoverTimeout !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(target.className !== 'cm-variable-valid') {
|
||||
return;
|
||||
}
|
||||
|
||||
const box = target.getBoundingClientRect();
|
||||
|
||||
const hoverTime = getHoverTime(cm);
|
||||
state.hoverTimeout = setTimeout(onHover, hoverTime);
|
||||
|
||||
const onMouseMove = function() {
|
||||
clearTimeout(state.hoverTimeout);
|
||||
state.hoverTimeout = setTimeout(onHover, hoverTime);
|
||||
};
|
||||
|
||||
const onMouseOut = function() {
|
||||
CodeMirror.off(document, 'mousemove', onMouseMove);
|
||||
CodeMirror.off(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
||||
clearTimeout(state.hoverTimeout);
|
||||
state.hoverTimeout = undefined;
|
||||
};
|
||||
|
||||
const onHover = function() {
|
||||
CodeMirror.off(document, 'mousemove', onMouseMove);
|
||||
CodeMirror.off(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
||||
state.hoverTimeout = undefined;
|
||||
onMouseHover(cm, box);
|
||||
};
|
||||
|
||||
CodeMirror.on(document, 'mousemove', onMouseMove);
|
||||
CodeMirror.on(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
||||
}
|
||||
|
||||
function onMouseHover(cm, box) {
|
||||
const pos = cm.coordsChar({
|
||||
left: (box.left + box.right) / 2,
|
||||
top: (box.top + box.bottom) / 2,
|
||||
});
|
||||
|
||||
const state = cm.state.brunoVarInfo;
|
||||
const options = state.options;
|
||||
const token = cm.getTokenAt(pos, true);
|
||||
if (token) {
|
||||
const brunoVarInfo = renderVarInfo(token, options, cm, pos);
|
||||
if (brunoVarInfo) {
|
||||
showPopup(cm, box, brunoVarInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showPopup(cm, box, brunoVarInfo) {
|
||||
const popup = document.createElement('div');
|
||||
popup.className = 'CodeMirror-brunoVarInfo';
|
||||
popup.appendChild(brunoVarInfo);
|
||||
document.body.appendChild(popup);
|
||||
|
||||
const popupBox = popup.getBoundingClientRect();
|
||||
const popupStyle = popup.currentStyle || window.getComputedStyle(popup);
|
||||
const popupWidth =
|
||||
popupBox.right -
|
||||
popupBox.left +
|
||||
parseFloat(popupStyle.marginLeft) +
|
||||
parseFloat(popupStyle.marginRight);
|
||||
const popupHeight =
|
||||
popupBox.bottom -
|
||||
popupBox.top +
|
||||
parseFloat(popupStyle.marginTop) +
|
||||
parseFloat(popupStyle.marginBottom);
|
||||
|
||||
let topPos = box.bottom;
|
||||
if (
|
||||
popupHeight > window.innerHeight - box.bottom - 15 &&
|
||||
box.top > window.innerHeight - box.bottom
|
||||
) {
|
||||
topPos = box.top - popupHeight;
|
||||
}
|
||||
|
||||
if (topPos < 0) {
|
||||
topPos = box.bottom;
|
||||
}
|
||||
|
||||
// make popup appear on top of cursor
|
||||
if (topPos > 70) {
|
||||
topPos = topPos - 70;
|
||||
}
|
||||
|
||||
let leftPos = Math.max(0, window.innerWidth - popupWidth - 15);
|
||||
if (leftPos > box.left) {
|
||||
leftPos = box.left;
|
||||
}
|
||||
|
||||
popup.style.opacity = 1;
|
||||
popup.style.top = topPos + 'px';
|
||||
popup.style.left = leftPos + 'px';
|
||||
|
||||
let popupTimeout;
|
||||
|
||||
const onMouseOverPopup = function() {
|
||||
clearTimeout(popupTimeout);
|
||||
};
|
||||
|
||||
const onMouseOut = function() {
|
||||
clearTimeout(popupTimeout);
|
||||
popupTimeout = setTimeout(hidePopup, 200);
|
||||
};
|
||||
|
||||
const hidePopup = function() {
|
||||
CodeMirror.off(popup, 'mouseover', onMouseOverPopup);
|
||||
CodeMirror.off(popup, 'mouseout', onMouseOut);
|
||||
CodeMirror.off(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
||||
|
||||
if (popup.style.opacity) {
|
||||
popup.style.opacity = 0;
|
||||
setTimeout(function() {
|
||||
if (popup.parentNode) {
|
||||
popup.parentNode.removeChild(popup);
|
||||
}
|
||||
}, 600);
|
||||
} else if (popup.parentNode) {
|
||||
popup.parentNode.removeChild(popup);
|
||||
}
|
||||
};
|
||||
|
||||
CodeMirror.on(popup, 'mouseover', onMouseOverPopup);
|
||||
CodeMirror.on(popup, 'mouseout', onMouseOut);
|
||||
CodeMirror.on(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user