Merge Jasons branch
This commit is contained in:
@@ -35,10 +35,12 @@ import { EditorState, Extension } from "@codemirror/state"
|
||||
import clone from "lodash/clone"
|
||||
import { tooltips } from "@codemirror/tooltip"
|
||||
import { history, historyKeymap } from "@codemirror/history"
|
||||
import { HoppRESTVar } from "@hoppscotch/data"
|
||||
import { inputTheme } from "~/helpers/editor/themes/baseTheme"
|
||||
import { HoppReactiveEnvPlugin } from "~/helpers/editor/extensions/HoppEnvironment"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import { AggregateEnvironment, aggregateEnvs$ } from "~/newstore/environments"
|
||||
import { HoppReactiveVarPlugin } from "~/helpers/editor/extensions/HoppVariable"
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -46,6 +48,7 @@ const props = withDefaults(
|
||||
placeholder: string
|
||||
styles: string
|
||||
envs: { key: string; value: string; source: string }[] | null
|
||||
vars: { key: string; value: string }[] | null
|
||||
focus: boolean
|
||||
readonly: boolean
|
||||
}>(),
|
||||
@@ -54,6 +57,7 @@ const props = withDefaults(
|
||||
placeholder: "",
|
||||
styles: "",
|
||||
envs: null,
|
||||
vars: null,
|
||||
focus: false,
|
||||
readonly: false,
|
||||
}
|
||||
@@ -120,7 +124,17 @@ const envVars = computed(() =>
|
||||
: aggregateEnvs.value
|
||||
)
|
||||
|
||||
const varVars = computed(() =>
|
||||
props.vars
|
||||
? props.vars.map((x) => ({
|
||||
key: x.key,
|
||||
value: x.value,
|
||||
}))
|
||||
: ([{ key: "size", value: "500" }] as HoppRESTVar[])
|
||||
)
|
||||
|
||||
const envTooltipPlugin = new HoppReactiveEnvPlugin(envVars, view)
|
||||
const varTooltipPlugin = new HoppReactiveVarPlugin(varVars, view)
|
||||
|
||||
const initView = (el: any) => {
|
||||
const extensions: Extension = [
|
||||
@@ -146,6 +160,7 @@ const initView = (el: any) => {
|
||||
position: "absolute",
|
||||
}),
|
||||
envTooltipPlugin,
|
||||
varTooltipPlugin,
|
||||
placeholderExt(props.placeholder),
|
||||
EditorView.domEventHandlers({
|
||||
paste(ev) {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
import { watch, Ref } from "@nuxtjs/composition-api"
|
||||
import { Compartment } from "@codemirror/state"
|
||||
import { hoverTooltip } from "@codemirror/tooltip"
|
||||
import {
|
||||
Decoration,
|
||||
EditorView,
|
||||
MatchDecorator,
|
||||
ViewPlugin,
|
||||
} from "@codemirror/view"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { HoppRESTVar, parseTemplateStringE } from "@hoppscotch/data"
|
||||
|
||||
const HOPP_ENVIRONMENT_REGEX = /({{\w+}})/g
|
||||
|
||||
const HOPP_ENV_HIGHLIGHT =
|
||||
"cursor-help transition rounded px-1 focus:outline-none mx-0.5 env-highlight"
|
||||
const HOPP_ENV_HIGHLIGHT_FOUND =
|
||||
"bg-accentDark text-accentContrast hover:bg-accent"
|
||||
const HOPP_ENV_HIGHLIGHT_NOT_FOUND =
|
||||
"bg-red-500 text-accentContrast hover:bg-red-600"
|
||||
|
||||
const cursorTooltipField = (aggregateEnvs: HoppRESTVar[]) =>
|
||||
hoverTooltip(
|
||||
(view, pos, side) => {
|
||||
const { from, to, text } = view.state.doc.lineAt(pos)
|
||||
|
||||
// TODO: When Codemirror 6 allows this to work (not make the
|
||||
// popups appear half of the time) use this implementation
|
||||
// const wordSelection = view.state.wordAt(pos)
|
||||
// if (!wordSelection) return null
|
||||
// const word = view.state.doc.sliceString(
|
||||
// wordSelection.from - 2,
|
||||
// wordSelection.to + 2
|
||||
// )
|
||||
// if (!HOPP_ENVIRONMENT_REGEX.test(word)) return null
|
||||
|
||||
// Tracking the start and the end of the words
|
||||
let start = pos
|
||||
let end = pos
|
||||
|
||||
while (start > from && /\w/.test(text[start - from - 1])) start--
|
||||
while (end < to && /\w/.test(text[end - from])) end++
|
||||
|
||||
if (
|
||||
(start === pos && side < 0) ||
|
||||
(end === pos && side > 0) ||
|
||||
!HOPP_ENVIRONMENT_REGEX.test(
|
||||
text.slice(start - from - 2, end - from + 2)
|
||||
)
|
||||
)
|
||||
return null
|
||||
|
||||
const envValue =
|
||||
aggregateEnvs.find(
|
||||
(env) => env.key === text.slice(start - from, end - from)
|
||||
// env.key === word.slice(wordSelection.from + 2, wordSelection.to - 2)
|
||||
)?.value ?? "not found"
|
||||
|
||||
const result = parseTemplateStringE(envValue, aggregateEnvs)
|
||||
|
||||
const finalEnv = E.isLeft(result) ? "error" : result.right
|
||||
|
||||
return {
|
||||
pos: start,
|
||||
end: to,
|
||||
above: true,
|
||||
arrow: true,
|
||||
create() {
|
||||
const dom = document.createElement("span")
|
||||
const xmp = document.createElement("xmp")
|
||||
xmp.textContent = finalEnv
|
||||
dom.appendChild(xmp)
|
||||
dom.className = "tooltip-theme"
|
||||
return { dom }
|
||||
},
|
||||
}
|
||||
},
|
||||
// HACK: This is a hack to fix hover tooltip not coming half of the time
|
||||
// https://github.com/codemirror/tooltip/blob/765c463fc1d5afcc3ec93cee47d72606bed27e1d/src/tooltip.ts#L622
|
||||
// Still doesn't fix the not showing up some of the time issue, but this is atleast more consistent
|
||||
{ hoverTime: 1 } as any
|
||||
)
|
||||
|
||||
function checkEnv(env: string, aggregateEnvs: HoppRESTVar[]) {
|
||||
const className = aggregateEnvs.find(
|
||||
(k: { key: string }) => k.key === env.slice(2, -2)
|
||||
)
|
||||
? HOPP_ENV_HIGHLIGHT_FOUND
|
||||
: HOPP_ENV_HIGHLIGHT_NOT_FOUND
|
||||
|
||||
return Decoration.mark({
|
||||
class: `${HOPP_ENV_HIGHLIGHT} ${className}`,
|
||||
})
|
||||
}
|
||||
|
||||
const getMatchDecorator = (aggregateEnvs: HoppRESTVar[]) =>
|
||||
new MatchDecorator({
|
||||
regexp: HOPP_ENVIRONMENT_REGEX,
|
||||
decoration: (m) => checkEnv(m[0], aggregateEnvs),
|
||||
})
|
||||
|
||||
export const environmentHighlightStyle = (aggregateEnvs: HoppRESTVar[]) => {
|
||||
const decorator = getMatchDecorator(aggregateEnvs)
|
||||
|
||||
return ViewPlugin.define(
|
||||
(view) => ({
|
||||
decorations: decorator.createDeco(view),
|
||||
update(u) {
|
||||
this.decorations = decorator.updateDeco(u, this.decorations)
|
||||
},
|
||||
}),
|
||||
{
|
||||
decorations: (v) => v.decorations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// export class HoppEnvironmentPlugin {
|
||||
// private compartment = new Compartment()
|
||||
//
|
||||
// private envs: AggregateEnvironment[] = []
|
||||
//
|
||||
// constructor(
|
||||
// subscribeToStream: StreamSubscriberFunc,
|
||||
// private editorView: Ref<EditorView | undefined>
|
||||
// ) {
|
||||
// this.envs = getAggregateEnvs()
|
||||
//
|
||||
// subscribeToStream(aggregateEnvs$, (envs) => {
|
||||
// this.envs = envs
|
||||
//
|
||||
// this.editorView.value?.dispatch({
|
||||
// effects: this.compartment.reconfigure([
|
||||
// cursorTooltipField(this.envs),
|
||||
// environmentHighlightStyle(this.envs),
|
||||
// ]),
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// get extension() {
|
||||
// return this.compartment.of([
|
||||
// cursorTooltipField(this.envs),
|
||||
// environmentHighlightStyle(this.envs),
|
||||
// ])
|
||||
// }
|
||||
// }
|
||||
|
||||
export class HoppReactiveVarPlugin {
|
||||
private compartment = new Compartment()
|
||||
|
||||
private envs: HoppRESTVar[] = []
|
||||
|
||||
constructor(
|
||||
envsRef: Ref<HoppRESTVar[]>,
|
||||
private editorView: Ref<EditorView | undefined>
|
||||
) {
|
||||
watch(
|
||||
envsRef,
|
||||
(envs) => {
|
||||
this.envs = envs
|
||||
|
||||
this.editorView.value?.dispatch({
|
||||
effects: this.compartment.reconfigure([
|
||||
cursorTooltipField(this.envs),
|
||||
environmentHighlightStyle(this.envs),
|
||||
]),
|
||||
})
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
}
|
||||
|
||||
get extension() {
|
||||
return this.compartment.of([
|
||||
cursorTooltipField(this.envs),
|
||||
environmentHighlightStyle(this.envs),
|
||||
])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user