mirror of
https://github.com/usebruno/bruno.git
synced 2025-06-25 06:21:57 +02: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-family: Inter, sans-serif !important;
|
||||||
font-weight: 400;
|
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}
|
.cm-variable-valid{color: green}
|
||||||
|
@ -24,6 +24,9 @@ class SingleLineEditor extends Component {
|
|||||||
lineNumbers: false,
|
lineNumbers: false,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
mode: "brunovariables",
|
mode: "brunovariables",
|
||||||
|
brunoVarInfo: {
|
||||||
|
variables: getEnvironmentVariables(this.props.collection),
|
||||||
|
},
|
||||||
extraKeys: {
|
extraKeys: {
|
||||||
"Enter": () => {
|
"Enter": () => {
|
||||||
if (this.props.onRun) {
|
if (this.props.onRun) {
|
||||||
@ -65,8 +68,7 @@ class SingleLineEditor extends Component {
|
|||||||
'Tab': () => {}
|
'Tab': () => {}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.editor.setValue(this.props.value)
|
this.editor.setValue(this.props.value);
|
||||||
|
|
||||||
this.editor.on('change', (cm) => {
|
this.editor.on('change', (cm) => {
|
||||||
this.props.onChange(cm.getValue());
|
this.props.onChange(cm.getValue());
|
||||||
});
|
});
|
||||||
@ -76,6 +78,7 @@ class SingleLineEditor extends Component {
|
|||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
let variables = getEnvironmentVariables(this.props.collection);
|
let variables = getEnvironmentVariables(this.props.collection);
|
||||||
if (!isEqual(variables, this.variables)) {
|
if (!isEqual(variables, this.variables)) {
|
||||||
|
this.editor.options.brunoVarInfo.variables = variables;
|
||||||
this.addOverlay();
|
this.addOverlay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ if (!SERVER_RENDERED) {
|
|||||||
require('codemirror-graphql/info');
|
require('codemirror-graphql/info');
|
||||||
require('codemirror-graphql/jump');
|
require('codemirror-graphql/jump');
|
||||||
require('codemirror-graphql/mode');
|
require('codemirror-graphql/mode');
|
||||||
|
|
||||||
|
require('utils/codemirror/brunoVarInfo');
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Main() {
|
export default function Main() {
|
||||||
|
@ -12,6 +12,7 @@ import '../styles/globals.css';
|
|||||||
import 'tailwindcss/dist/tailwind.min.css';
|
import 'tailwindcss/dist/tailwind.min.css';
|
||||||
import 'codemirror/lib/codemirror.css';
|
import 'codemirror/lib/codemirror.css';
|
||||||
import 'graphiql/graphiql.min.css';
|
import 'graphiql/graphiql.min.css';
|
||||||
|
import 'utils/codemirror/brunoVarInfo.css';
|
||||||
|
|
||||||
function SafeHydrate({ children }) {
|
function SafeHydrate({ children }) {
|
||||||
return <div suppressHydrationWarning>{typeof window === 'undefined' ? null : children}</div>;
|
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…
x
Reference in New Issue
Block a user