diff --git a/src/common/ct_event.nim b/src/common/ct_event.nim index ba532137c..dd025a16b 100644 --- a/src/common/ct_event.nim +++ b/src/common/ct_event.nim @@ -60,6 +60,7 @@ type CtUpdatedFlow, CtRunToEntry, CtRunTracepoints, + CtRunTraceSession, CtSetupTraceSession, CtLoadAsmFunction, CtLoadAsmFunctionResponse, @@ -68,6 +69,7 @@ type InternalAddToScratchpadFromExpression, InternalStatusUpdate, InternalNewOperation, + InternalTraceMapUpdate, CtNotification, when defined(js): diff --git a/src/frontend/dap.nim b/src/frontend/dap.nim index 058a9e07f..009e177b0 100644 --- a/src/frontend/dap.nim +++ b/src/frontend/dap.nim @@ -100,6 +100,7 @@ const EVENT_KIND_TO_DAP_MAPPING: array[CtEventKind, cstring] = [ CtUpdatedFlow: "ct/updated-flow", CtRunToEntry: "ct/run-to-entry", CtRunTracepoints: "ct/run-tracepoints", + CtRunTraceSession: "ct/run-trace-session", CtSetupTraceSession: "ct/setup-trace-session", CtLoadAsmFunction: "ct/load-asm-function", CtLoadAsmFunctionResponse: "", @@ -108,6 +109,7 @@ const EVENT_KIND_TO_DAP_MAPPING: array[CtEventKind, cstring] = [ InternalAddToScratchpadFromExpression: "", InternalStatusUpdate: "", InternalNewOperation: "", + InternalTraceMapUpdate: "", CtNotification: "ct/notification", ] diff --git a/src/frontend/middleware.nim b/src/frontend/middleware.nim index 75fd92e31..14f0efd47 100644 --- a/src/frontend/middleware.nim +++ b/src/frontend/middleware.nim @@ -5,6 +5,38 @@ import communication, dap import event_helpers # backend(dap) <-> middleware <-> view (self-contained, can be separate: 0, 1 or more components); +var tracepointMap: JsAssoc[cstring, JsAssoc[int, Tracepoint]] = Jsassoc[cstring, JsAssoc[int, Tracepoint]]{} +var sessionCounter = 0 + +proc getTraceSession(name: cstring): TraceSession = + var results = JsAssoc[int, seq[Stop]]{} + var tracepoints: seq[Tracepoint] = @[] + + for line, trace in tracepointMap[name]: + if trace.expression != "" and trace.isChanged: + tracepoints.add(trace) + + result = TraceSession( + tracepoints: tracepoints, + lastCount: 0, + results: results, + id: sessionCounter + ) + sessionCounter += 1 + + return result + +proc updateTraceMap(tracepoint: Tracepoint) = + if not tracepointMap.hasKey(tracepoint.name): + tracepointMap[tracepoint.name] = JsAssoc[int, Tracepoint]{} + tracepointMap[tracepoint.name][tracepoint.line] = tracepoint + return + + if not tracepointMap[tracepoint.name].hasKey(tracepoint.line): + tracepointMap[tracepoint.name][tracepoint.line] = tracepoint + return + + tracepointMap[tracepoint.name][tracepoint.line] = tracepoint when not defined(ctInExtension): import utils @@ -149,6 +181,19 @@ proc setupMiddlewareApis*(dapApi: DapApi, viewsApi: MediatorWithSubscribers) {.e if not lastCompleteMove.isNil: viewsApi.emit(CtCompleteMove, lastCompleteMove.toJs) ) + viewsApi.subscribe(InternalTraceMapUpdate, proc(kind: CtEventKind, value: Tracepoint, sub: Subscriber) = + updateTraceMap(value) + ) + viewsApi.subscribe(CtRunTraceSession, proc(kind: CtEventKind, value: SourceLocation, sub: Subscriber) = + let traceSession = getTraceSession(value.path) + dapApi.sendCtRequest( + CtRunTracepoints, + RunTracepointsArg( + session: traceSession, + stopAfter: NO_LIMIT + ).toJs + ) + ) when defined(ctInExtension): when defined(ctInCentralExtensionContext): @@ -165,3 +210,4 @@ when defined(ctInExtension): {.emit: "module.exports.getRecentTransactions = getRecentTransactions".} {.emit: "module.exports.getTransactionTrace = getTransactionTrace".} {.emit: "module.exports.getCurrentTrace = getCurrentTrace".} + {.emit: "module.exports.getFlowList = getFlowList".} diff --git a/src/frontend/styles/components/flow.styl b/src/frontend/styles/components/flow.styl index b953a67d1..f55769cd6 100644 --- a/src/frontend/styles/components/flow.styl +++ b/src/frontend/styles/components/flow.styl @@ -212,15 +212,24 @@ position: absolute width: 100px !important +.flow-component-container + transform: translate(0px, 2px) + .flow-loop-slider border-color: FLOW_PARALLEL_SLIDER_COLOR !important border-width: 1px !important position: fixed !important font-size: 0.9em height: 2px !important + if IS_EXTENSION + transform: translate(0px, 8px) .noUi-base - width: 100% + if IS_EXTENSION + width: 550px + margin-left: 220px + else + width: 100% height: 2px !important background-color: NOUI_SLIDER_BACKGROUND_COLOR @@ -237,6 +246,8 @@ background-repeat: no-repeat border-radius: 100px outline: 2px solid NOUI_SLIDER_HANDLER_BORDER + if IS_EXTENSION + transform: translate(620px, -4px) .noUi-connect background: transparent .noUi-tooltip diff --git a/src/frontend/styles/components/text_editor.styl b/src/frontend/styles/components/text_editor.styl index f28985598..61f472d68 100644 --- a/src/frontend/styles/components/text_editor.styl +++ b/src/frontend/styles/components/text_editor.styl @@ -869,3 +869,118 @@ else .inactive-tab display: none + +.flow-multiline-value-container + position: absolute + // background-color: FLOW_PARALLEL_BACKGROUND_COLOR + height: 20px + width: fit-content + // min-width: 20px + // max-width: 250px + z-index:1 + display: flex + align-items: center + +button + background-color: PRIMARY_BASE + padding-left: 10px + color: PRIMARY_TEXT_COLOR + padding-right: 10px + // height: 100% + // font-size: 13px + font-family: "FiraCode" + margin: 1px !important + margin-left: 2px !important + display: flex + align-items: center + border-radius: 4px !important + cursor: pointer !important + background-position: center + background-repeat: no-repeat + if IS_EXTENSION + background-size: 8px + else + background-size: 1ch + + &:hover + background-color: ACTIVE_MENU_BACKGROUND + + &:disabled + opacity: 0.5 !important + cursor: default !important + + &:hover + background-color: PRIMARY_BASE + + &.backward + background-image: url('../../public/resources/shared/omniscience_backward_dark.svg') + + &.forward + background-image: url('../../public/resources/shared/omniscience_forward_dark.svg') + +.flow-parallel-value + display: inline-flex + padding-right: 0 + align-items: center + margin-right: 10px + +.flow-parallel-value-name + // font-size: 13px + font-family: "FiraCode" + background: FLOW_LOOP_EXPRESSION_BACKGROUND_COLOR + // box-shadow: 1px 1px 1px FLOW_LOOP_EXPRESSION_BOX_SHADOW_COLOR + color: FLOW_PARALLEL_NAME_COLOR + padding-left: 5px + padding-right: 5px + padding-top: 0 + padding-bottom: 0 + // height: 17px + // line-height: 17px + display: flex + border-radius: 2px + box-sizing: border-box + white-space: nowrap + +.flow-loop.value + font-size: inherit + + span + font-size: inherit + +.flow-loop-value-name + // font-size: 13px + font-family: "FiraCode" + background: PRIMARY_BASE + box-shadow: 1px 1px 1px FLOW_LOOP_EXPRESSION_BOX_SHADOW_COLOR + color: PRIMARY_TEXT_COLOR + padding-left: 5px + padding-right: 5px + padding-top: 0 + padding-bottom: 0 + margin-right: 0px !important + // height: 17px + // line-height: 17px + display: flex + border-radius: 2px + box-sizing: border-box + white-space: nowrap + +.flow-loop-textarea + height: 100% + background-color: FLOW_LOOP_EXPRESSION_BACKGROUND_COLOR + font-family: monospace + border: 0 + padding: 0 + padding-right: 3px + overflow: hidden + resize: none + color: FLOW_PARALLEL_NAME_COLOR + outline: none + cursor: text + opacity: 0.7 + + &:focus + opacity: 1 + + &::placeholder + color: FLOW_PARALLEL_NAME_COLOR diff --git a/src/frontend/styles/components/tracepoint.styl b/src/frontend/styles/components/tracepoint.styl index 57dc9c220..7d3d835f7 100644 --- a/src/frontend/styles/components/tracepoint.styl +++ b/src/frontend/styles/components/tracepoint.styl @@ -53,6 +53,8 @@ padding-right: 1ch // height: 36ch position: absolute + if IS_EXTENSION + max-width: calc(100% - 15px) // left: -81px .editor-textarea @@ -255,10 +257,6 @@ .fa color: DROPDOWN_TEXT_BACKGROUND_COLOR - .trace-buttons-container - display: flex - gap: 6px - .run-trace-button border-radius: 6px; color: BORDER_COLOR; @@ -301,6 +299,14 @@ z-index: TRACE_LOG_EDITOR_Z_INDEX + 7 top: 0 + .trace-hamburger-svg + height: 24px + width: 24px + background-image: TRACE_HAMBURGER_IMAGE + background-size: 12px 8px + background-repeat: no-repeat + background-position: center + .trace-dropdown-menu display: none @@ -364,6 +370,16 @@ margin: 0 padding: 0 + .trace-buttons-container + display: flex + gap: 6px + + .trace-run-button-svg + height: 24px + width: 24px + background-image: RUN_TRACEPOINT_IMAGE + background-repeat: no-repeat + .kind-dropdown-menu background: #b8c0cf !important position: absolute diff --git a/src/frontend/styles/default_dark_theme.styl b/src/frontend/styles/default_dark_theme.styl index 594bbe024..5f287a527 100644 --- a/src/frontend/styles/default_dark_theme.styl +++ b/src/frontend/styles/default_dark_theme.styl @@ -21,6 +21,9 @@ BREAKPOINT_COLOR=#F87171 TRACEPOINT_COLOR=#6366F1 TRACEPOINT_LINE_NUMBER_COLOR=#c6c6c6 +RUN_TRACEPOINT_IMAGE=url("../../public/resources/tracepoints/run_tracepoints_dark.svg") +TRACE_HAMBURGER_IMAGE=url("../../public/resources/tracepoints/trace_hamburger_dark.svg") + NOUI_SLIDER_HANDLER_IMAGE=url("../../public/resources/shared/noUiSlider_handle_icon_dark.svg") NOUI_SLIDER_ACTIVE_HANDLER_IMAGE=url("../../public/resources/shared/noUiSlider_handle_active_icon_dark.svg") NOUI_SLIDER_HANDLER_BACKGROUND=#818181 diff --git a/src/frontend/types.nim b/src/frontend/types.nim index f472a8ff5..8fb172967 100644 --- a/src/frontend/types.nim +++ b/src/frontend/types.nim @@ -1044,6 +1044,7 @@ type tippyElement*: JsObject leftPos*: cstring lastScrollFireTime*: int64 + position*: int # codeID*: int64 diff --git a/src/frontend/ui/flow.nim b/src/frontend/ui/flow.nim index 8a957c066..38e518b1e 100644 --- a/src/frontend/ui/flow.nim +++ b/src/frontend/ui/flow.nim @@ -22,6 +22,7 @@ proc redrawFlow*(self: FlowComponent) proc resizeFlowSlider*(self: FlowComponent) proc makeSlider(self: FlowComponent, position: int) proc updateFlowOnMove*(self: FlowComponent, rrTicks: int, line: int) +proc makeLoopLine(self: FlowComponent, step: FlowStep, allIterations: int): VNode const SLIDER_OFFSET = 6 # in px const FLOW_VALUE_LIMIT = 30 @@ -38,6 +39,40 @@ proc getFlowValueMode(self: FlowComponent, beforeValue: Value, afterValue: Value else: return BeforeAndAfterValueMode +when defined(ctInExtension): + var flowComponentForExtension* {.exportc.}: FlowComponent = makeFlowComponent(data, 13, inExtension = true) + + proc makeFlowComponentForExtension*(id: cstring): FlowComponent {.exportc.} = + if flowComponentForExtension.kxi.isNil: + flowComponentForExtension.kxi = setRenderer(proc: VNode = flowComponentForExtension.render(), id, proc = discard) + result = flowComponentForExtension + +method register*(self: FlowComponent, api: MediatorWithSubscribers) = + self.api = api + api.subscribe(CtCompleteMove, proc(kind: CtEventKind, response: MoveState, sub: Subscriber) = + self.location = response.location + api.emit(CtLoadFlow, self.location) + self.redraw() + ) + api.subscribe(CtUpdatedFlow, proc(kind: CtEventKind, response: FlowUpdate, sub: Subscriber) = + discard self.onUpdatedFlow(response) + self.redrawFlow() + self.redraw() + ) + +method render*(self: FlowComponent): VNode = + let allIterations = if self.flow.isNil: 0 else: self.flow.loops[self.activeStep.loop].rrTicksForIterations.len - 1 + result = buildHtml(tdiv(class="flow-component-container")): + makeLoopLine(self, self.activeStep, allIterations) + tdiv( + class = "flow-loop-slider-container", + id = fmt"flow-loop-slider-container-{self.position}" + ) + + +proc registerFlowComponent*(component: FlowComponent, api: MediatorWithSubscribers) {.exportc.} = + component.register(api) + proc getStepDomOffsetLeft*(self: FlowComponent, step: FlowStep): float = let flowLine = self.flowLines[step.position] let loopState = self.loopStates[step.loop] @@ -457,7 +492,7 @@ proc openValue*(self: FlowComponent, stepCount: int, name: cstring, before: bool if step.beforeValues.hasKey(name): self.scratchpadUI.registerScratchpadValue(name, step.beforeValues[name]) - self.data.redraw() + self.redraw() else: if name == cstring"": @@ -467,7 +502,7 @@ proc openValue*(self: FlowComponent, stepCount: int, name: cstring, before: bool if step.afterValues.hasKey(name): self.scratchpadUI.registerScratchpadValue(name, step.afterValues[name]) - self.data.redraw() + self.redraw() proc displayTooltip(self: FlowComponent, containerId: cstring, content: Node) = when not defined(server) and not defined(ctInCentralExtensionContext): @@ -624,6 +659,7 @@ proc getOriginLoopIndex*(self: FlowComponent, loopIndex: int): int = self.getOriginLoopIndex(parentId) proc calculateFlowLineLeftOffset(self:FlowComponent, flowLine: FlowLine): int = + if self.inExtension: return 0 case self.data.config.flow.realFlowUI: of FlowParallel, FlowInline: var flowLineOffset = @@ -1043,7 +1079,7 @@ proc moveRight*(self: FlowComponent) = self.selectedStepCount = stepCount self.selectedGroup.visibleStart = self.selectedIndex.float * self.selectedGroup.baseWidth - self.data.redraw() + self.redraw() proc moveLeft*(self: FlowComponent) = if self.selectedIndex > 0: @@ -1051,7 +1087,7 @@ proc moveLeft*(self: FlowComponent) = self.selectedStepCount = self.findStepCount() self.selectedGroup.visibleStart = self.selectedIndex.float * self.selectedGroup.baseWidth - self.data.redraw() + self.redraw() method onRight*(self: FlowComponent) {.async.} = @@ -1104,7 +1140,7 @@ proc jumpToLocalStep*(self: FlowComponent, path: cstring, line: int, stepCount: proc afterJump(self: FlowComponent, stepCount: int) = let step = self.flow.steps[stepCount] - let location = self.data.services.debugger.location + let location = self.location let currentStep = self.positionRRTicksToStepCount(location.highLevelLine, location.rrTicks) let reverse = if stepCount >= currentStep: false else: true @@ -1113,7 +1149,7 @@ proc afterJump(self: FlowComponent, stepCount: int) = if lastTimePlusDelay <= currentTime: self.redrawFlow() - self.jumpToLocalStep(self.tab.name, step.position, stepCount, step.iteration, step.rrTicks, reverse) + self.jumpToLocalStep(self.location.path, step.position, stepCount, step.iteration, step.rrTicks, reverse) proc jumpToLocalStep*(self: FlowComponent, stepCount: int) = @@ -1464,9 +1500,10 @@ proc flowSimpleValue*( text afterValue.textRepr(compact=true) proc clearSliders(self: FlowComponent) = - var tab = self.data.services.editor.open[self.editorUI.path] - for widget in self.sliderWidgets: - tab.monacoEditor.removeContentWidget(widget) + if not self.inExtension: + var tab = self.data.services.editor.open[self.editorUI.path] + for widget in self.sliderWidgets: + tab.monacoEditor.removeContentWidget(widget) self.sliderWidgets = JsAssoc[int, js]{} proc clearInline(self: FlowComponent) = @@ -1485,17 +1522,18 @@ proc clearInline(self: FlowComponent) = line.contentWidget = nil proc clearParallel(self: FlowComponent) = - var tab = self.data.services.editor.open[self.editorUI.path] - - if not tab.monacoEditor.isNil: - for viewZone in self.loopViewZones: - tab.monacoEditor.changeViewZones do (view: js): - view.removeZone(viewZone) - # clear flow line content widgets - for flowLine in self.flowLines: - if not flowLine.contentWidget.isNil: - tab.monacoEditor.removeContentWidget(flowLine.contentWidget.toJs) - flowLine.contentWidget = nil + if not self.inExtension: + var tab = self.data.services.editor.open[self.editorUI.path] + + if not tab.monacoEditor.isNil: + for viewZone in self.loopViewZones: + tab.monacoEditor.changeViewZones do (view: js): + view.removeZone(viewZone) + # clear flow line content widgets + for flowLine in self.flowLines: + if not flowLine.contentWidget.isNil: + tab.monacoEditor.removeContentWidget(flowLine.contentWidget.toJs) + flowLine.contentWidget = nil self.flowDom = JsAssoc[int, Node]{} self.lineWidgets = JsAssoc[int, JsObject]{} self.flowLoops = JsAssoc[int, FlowLoop]{} @@ -1529,12 +1567,13 @@ proc clearViewZones(self: FlowComponent) = proc resetFlow*(self: FlowComponent) = self.clearSliders() - self.clearInline() + # self.clearInline() self.clearMultiline() self.clearParallel() self.clearLoopStates() - self.clearWidgets() + if not self.inExtension: + self.clearWidgets() self.clearFlowLines() self.clearStepNodes() self.clearViewZones() @@ -1566,7 +1605,7 @@ proc switchFlowUI*(self: FlowComponent, flowUI: FlowUI) = self.data.config.flow.ui = flowUINames[flowUI] self.data.config.flow.realFlowUI = flowUI - self.data.redraw() + self.redraw() proc addContentWidget*( self: FlowComponent, @@ -2918,10 +2957,10 @@ proc flowLoopValue*( allIterations: int, style: VStyle ): VNode = - let flowMode = - ($self.data.config.flow.realFlowUI) - .substr(4, ($self.data.config.flow.realFlowUI).len - 1) - .toLowerAscii() + # let flowMode = + # ($self.data.config.flow.realFlowUI) + # .substr(4, ($self.data.config.flow.realFlowUI).len - 1) + # .toLowerAscii() var iteration = step.iteration var width = len(intToStr(allIterations)) @@ -2954,7 +2993,8 @@ proc flowLoopValue*( (StyleAttr.width, cstring($(width+1) & "ch")), (StyleAttr.textAlign, cstring("right"))), ) - span(class = &"flow-{flowMode}-loop-iteration-end"): text fmt"from {allIterations}" + # TODO: FOR NOW HARDCODE THE PARALLEL + span(class = &"flow-parallel-loop-iteration-end"): text fmt"from {allIterations}" proc backLoopControlButton(self: FlowComponent, step: FlowStep, style: VStyle): VNode = let iteration = step.iteration @@ -2970,7 +3010,7 @@ proc backLoopControlButton(self: FlowComponent, step: FlowStep, style: VStyle): self.activeStep = previousIterationStepCount self.jumpToLocalStep(self.activeStep.stepCount + 1) self.redrawFlow() - self.data.redraw() + self.redraw() ) ) @@ -2989,7 +3029,7 @@ proc nextLoopControlButton(self: FlowComponent, step: FlowStep, style: VStyle): self.activeStep = nextIterationStepCount self.jumpToLocalStep(self.activeStep.stepCount + 1) self.redrawFlow() - self.data.redraw() + self.redraw() ) ) @@ -2997,15 +3037,13 @@ proc makeLoopLine( self: FlowComponent, step: FlowStep, allIterations: int -): Node = - let editor = self.editorUI.monacoEditor - let positionColumn = editor.getOffsetForColumn(step.position, 0) - let editorConfiguration = editor.config - let editorLeftOffset = editorConfiguration.layoutInfo.contentLeft +): VNode = + let fontSize = if self.fontSize != 0: cstring($(self.fontSize) & "px") else: cstring("inherit") let style = style( - (StyleAttr.fontSize, cstring($(self.fontSize) & "px")), + (StyleAttr.fontSize, fontSize), (StyleAttr.lineHeight, cstring($self.lineHeight & "px")), - (StyleAttr.height, cstring($self.lineHeight & "px")) + (StyleAttr.height, cstring($self.lineHeight & "px")), + (StyleAttr.width, cstring("fit-content")) ) let vNode = buildHtml( @@ -3014,21 +3052,22 @@ proc makeLoopLine( class = "flow-multiline-value-container" ) ): - backLoopControlButton(self, step, style) - flowLoopValue(self, step, allIterations, style) - nextLoopControlButton(self, step, style) + if step.rrTicks != -1: + backLoopControlButton(self, step, style) + flowLoopValue(self, step, allIterations, style) + nextLoopControlButton(self, step, style) - self.data.redraw() + # self.redraw() - return vnodeToDom(vNode, KaraxInstance()) + return vNode proc makeFlowLoops(self: FlowComponent, step: FlowStep) = let expression = &"for-{step.position}" # render variable lines in the viewZone let allIterations = self.flow.loops[step.loop].rrTicksForIterations.len - 1 - let dom = self.makeLoopLine( + let dom = vnodeToDom(self.makeLoopLine( step, - allIterations) + allIterations), KaraxInstance()) cast[Node](self.flowLoops[step.position].flowZones.dom).appendChild(dom) self.flowLoops[step.position].flowDom = dom @@ -3038,8 +3077,6 @@ proc addLoopInfo(self: FlowComponent, step: FlowStep) = # create viewZone for this step if there is not any yet if not self.flowLoops.hasKey(step.position): self.flowLoops[step.position] = FlowLoop(loopStep: step) - let lineHeight = - self.editorUI.monacoEditor.config.lineHeight let position = self.flow.loops[step.loop].first let newZoneDom = createFlowViewZone( @@ -3080,14 +3117,14 @@ proc updateIterationStepCount*(self: FlowComponent, line: int, stepCount: int, l proc getCurrentStepCount*(self: FlowComponent, line: int): int = var stepCount: int - stepCount = self.positionRRTicksToStepCount(line, self.data.services.debugger.location.rrTicks) + stepCount = self.positionRRTicksToStepCount(line, self.location.rrTicks) let step = self.flow.steps[stepCount] if self.flowLoops.hasKey(self.flow.loops[step.loop].first): let loopStep = self.flowLoops[self.flow.loops[step.loop].first].loopStep stepCount = self.updateIterationStepCount(line, stepCount, loopStep.loop, loopStep.iteration) elif self.activeStep.rrTicks == NO_TICKS: - stepCount = self.positionRRTicksToStepCount(line, self.data.services.debugger.location.rrTicks) + stepCount = self.positionRRTicksToStepCount(line, self.location.rrTicks) else: stepCount = self.positionRRTicksToStepCount(line, self.activeStep.rrTicks) let activeStep = self.flow.steps[stepCount] @@ -3110,7 +3147,10 @@ proc getCurrentStepCount*(self: FlowComponent, line: int): int = proc renderFlowLines*(self: FlowComponent) = # cdebug "flow: renderFlowLines" let editorContentLeft = - self.editorUI.monacoEditor.config.layoutInfo.contentLeft.float + if self.inExtension: + 0.0 + else: + self.editorUI.monacoEditor.config.layoutInfo.contentLeft.float self.createLoopStates() @@ -3120,32 +3160,35 @@ proc renderFlowLines*(self: FlowComponent) = let loopId = step.loop let loopIteration = step.iteration - # calculate variables position on the line - if toSeq(self.flowLines[step.position].variablesPositions.keys()).len == 0: - for expression, values in step.beforeValues: - discard calculateVariablePosition(self, step.position, expression) - self.sortVariablesPositions(step, false) + # TODO: FOR NOW DISCARD THIS PART: calculate variables position on the line + # if toSeq(self.flowLines[step.position].variablesPositions.keys()).len == 0: + # for expression, values in step.beforeValues: + # discard calculateVariablePosition(self, step.position, expression) + # self.sortVariablesPositions(step, false) # add step values - let monacoEditorRange = self.editorUI.monacoEditor.getVisibleRanges()[0] - let flowViewStartLine = monacoEditorRange.startLineNumber.to(int) - let flowViewEndLine = monacoEditorRange.endLineNumber.to(int) + # let monacoEditorRange = self.editorUI.monacoEditor.getVisibleRanges()[0] + # let flowViewStartLine = monacoEditorRange.startLineNumber.to(int) + # let flowViewEndLine = monacoEditorRange.endLineNumber.to(int) if not self.stepNodes.hasKey(step.stepCount): if step.position == self.flow.loops[loopId].registeredLine: self.addLoopInfo(step) - if step.beforeValues.len > 0 or step.afterValues.len > 0 or step.events.len > 0: - self.addStepValues(step) + # if step.beforeValues.len > 0 or step.afterValues.len > 0 or step.events.len > 0: + # self.addStepValues(step) proc reloadFlow*(self:FlowComponent) = self.renderFlowLines() proc createFlowLines(self: FlowComponent) = let editorContentLeft = - self.editorUI.monacoEditor.config.layoutInfo.contentLeft.float + if self.inExtension: + 0.0 + else: + self.editorUI.monacoEditor.config.layoutInfo.contentLeft.float for line, stepCounts in self.flow.positionStepCounts: - if line < self.tab.sourceLines.len: + if self.inExtension or line < self.tab.sourceLines.len: for stepCount in stepCounts: let step = self.flow.steps[stepCount] if step.loop == 0 and step.iteration == 0: @@ -3197,10 +3240,11 @@ proc calculateLineHeight(self: FlowComponent) = proc recalculateAndRedrawFlow*(self: FlowComponent) = if not self.flow.isNil: self.createFlowLines() - self.calculateLineHeight() + if not self.inExtension: + self.calculateLineHeight() self.renderFlowLines() - if self.mutationObserver.isNil: + if self.mutationObserver.isNil and not self.inExtension: setEditorMutationObserver(self) proc adjustFlow(self: FlowComponent) = @@ -3220,35 +3264,36 @@ method onUpdatedFlow*(self: FlowComponent, update: FlowUpdate) {.async.} = if update.location.toJs.isNil: cdebug "flow: update location is nil: stopping" return - let updateLocationName = if self.editorUI.editorView != ViewInstructions: + let updateLocationName = if self.inExtension or self.editorUI.editorView != ViewInstructions: update.location.highLevelPath else: # should be always path:name update.location.highLevelPath & cstring":" & update.location.functionName - if self.editorUI.name != updateLocationName: + if not self.inExtension and self.editorUI.name != updateLocationName: cdebug "flow: editor name not equal to update location name: stopping" return self.status = update.status + let editorView = if self.inExtension: EditorView.ViewSource else: self.editorUI.editorView if update.location.key != self.key: self.resetFlow() self.key = update.location.key - if self.flow.isNil: - self.flow = update.view_updates[self.editorUI.editorView] + self.flow = update.view_updates[editorView] else: - self.flow = update.view_updates[self.editorUI.editorView] + self.flow = update.view_updates[editorView] - self.editorUI.flowUpdate = update + if not self.inExtension: + self.editorUI.flowUpdate = update self.recalculateAndRedrawFlow() self.redrawFlow() self.recalculate = true - self.data.redraw() + self.redraw() except: console.error lastJSError console.error lastJSError.stack @@ -3261,23 +3306,26 @@ proc varStyle(self: FlowComponent, fields: seq[cstring]): VStyle = proc makeSliderDom(self: FlowComponent, position: int): Node = var dom = cast[Node](jq(&"#flow-loop-slider-container-{position}")) - + var style = if self.inExtension: style() else: loopSliderStyle(self, position) + var leftStyle = if self.inExtension: style() else: flowLeftStyle(self, position, true) if dom.isNil: let vNode = buildHtml(tdiv(class = "flow-loop-slider-container", id = &"flow-loop-slider-container-{position}", - style = flowLeftStyle(self, position, true))): + style = leftStyle)): tdiv(class = "flow-loop-slider", id = &"flow-loop-slider-{position}", - style = loopSliderStyle(self, position)) + style = style) dom = vnodeToDom(vNode, KaraxInstance()) else: - let childVNode = buildHtml(tdiv(class = "flow-loop-slider", - id = &"flow-loop-slider-{position}", - style = loopSliderStyle(self, position))): text "" - let childDom = vnodeToDom(childVNode, KaraxInstance()) + let domCheck = cast[Node](jq(&"flow-loop-slider-{position}")) + if domCheck.isNil: + let childVNode = buildHtml(tdiv(class = "flow-loop-slider", + id = &"flow-loop-slider-{position}", + style = style)): text "" + let childDom = vnodeToDom(childVNode, KaraxInstance()) - dom.appendChild(childDom) + dom.appendChild(childDom) self.flowLoops[position].sliderDom = dom.childNodes[0] @@ -3287,7 +3335,7 @@ proc addSliderWidget(self: FlowComponent, position:int) = let id = &"flow-slider-widget-{position}" let dom = makeSliderDom(self, position) - self.flowLoops[position].flowDom.appendChild(dom) + self.flowLoops[position].flowDom = dom proc resizeEditorHandler(self:FlowComponent, position: int) = # get new monaco editor config @@ -3317,35 +3365,35 @@ proc calculateLineIndentations(self: FlowComponent, position: int) : int = proc createFlowViewZone(self: FlowComponent, position: int, heightInPx: float, isLoop: bool = false): Node = #create viewZone - let editorLineNumbersWidth = - self.editorUI.monacoEditor.config.layoutInfo.contentLeft var zoneDom = document.createElement("div") zoneDom.id = fmt"flow-view-zone-{position}" zoneDom.class = "flow-view-zone" zoneDom.style.display = "flex" - let viewZone = js{ - afterLineNumber: position, - heightInPx: heightInPx + 3, - domNode: zoneDom - } - - if isLoop: - self.editorUI.monacoEditor.changeViewZones do (view: js): - var zoneId = cast[int](view.addZone(viewZone)) - self.loopViewZones[position] = zoneId - else: - self.editorUI.monacoEditor.changeViewZones do (view: js): - var zoneId = cast[int](view.addZone(viewZone)) - self.viewZones[position] = zoneId + if not self.inExtension: + let viewZone = js{ + afterLineNumber: position, + heightInPx: heightInPx + 3, + domNode: zoneDom + } + + if isLoop: + self.editorUI.monacoEditor.changeViewZones do (view: js): + var zoneId = cast[int](view.addZone(viewZone)) + self.loopViewZones[position] = zoneId + else: + self.editorUI.monacoEditor.changeViewZones do (view: js): + var zoneId = cast[int](view.addZone(viewZone)) + self.viewZones[position] = zoneId # calculate previous position indentations count let lineNumberDom = document.createElement("div") lineNumberDom.class = "line-numbers" lineNumberDom.style.height = "100%" - lineNumberDom.style.width = jq(".line-numbers").style.width + if not self.inExtension: + lineNumberDom.style.width = jq(".line-numbers").style.width lineNumberDom.style.position = "absolute" lineNumberDom.style.left = "0px" zoneDom.appendChild(lineNumberDom) @@ -3371,7 +3419,6 @@ proc makeSlider(self: FlowComponent, position: int) = var element = self.flowLoops[position].sliderDom let step = self.flowLoops[position].loopStep let loop = self.flow.loops[step.loop] - var maxLength = self.editorUI.monacoEditor.config.layoutInfo.contentWidth # tooltip function proc sliderEncoder(value: float): int = @@ -3381,7 +3428,7 @@ proc makeSlider(self: FlowComponent, position: int) = return if not element.toJs.noUiSlider.isNil: - element.toJs.noUiSlider.destroy() + element.toJs.noUiSlider.destroy() else: noUiSlider.create(element, js{ "start": step.iteration, @@ -3400,8 +3447,8 @@ proc makeSlider(self: FlowComponent, position: int) = let newStepCount = self.flow.loopIterationSteps[step.loop][loopIteration].table[step.position] let activeStep = self.flow.steps[newStepCount] - if self.data.ui.activeFocus != self: - self.data.ui.activeFocus = self + # if self.data.ui.activeFocus != self: + # self.data.ui.activeFocus = self self.flowLoops[position].loopStep = activeStep self.activeStep = activeStep @@ -3417,7 +3464,8 @@ proc makeSlider(self: FlowComponent, position: int) = let elementSlider = cast[JsObject](element).noUiSlider elementSlider.on(cstring"slide", onUpdate) - setEditorResizeObserver(self, position) + if not self.inExtension: + setEditorResizeObserver(self, position) proc resizeLineSlider(self: FlowComponent, position: int) = let editor = self.editorUI.monacoEditor diff --git a/src/frontend/ui/scratchpad.nim b/src/frontend/ui/scratchpad.nim index 2b9f9641a..474748f52 100644 --- a/src/frontend/ui/scratchpad.nim +++ b/src/frontend/ui/scratchpad.nim @@ -84,3 +84,6 @@ method render*(self: ScratchpadComponent): VNode = else: tdiv(class = "empty-overlay"): text "You can add values from other components by right clicking on them and then click on 'Add value to scratchpad'." + +proc registerScratchpadComponent*(component: ScratchpadComponent, api: MediatorWithSubscribers) {.exportc.} = + component.register(api) diff --git a/src/frontend/ui/trace.nim b/src/frontend/ui/trace.nim index c2659da21..f58bac709 100644 --- a/src/frontend/ui/trace.nim +++ b/src/frontend/ui/trace.nim @@ -17,6 +17,23 @@ proc toggleTrace*(editorUI: EditorViewComponent, name: cstring, line: int) proc closeTrace*(self: TraceComponent) proc resizeTraceHandler(self: TraceComponent) +when defined(ctInExtension): + var tracepointComponentMapping* {.exportc.}: JsAssoc[cstring, JsAssoc[int, TraceComponent]] = JsAssoc[cstring, JsAssoc[int, TraceComponent]]{} + # var tracepointComponentSeq* {.exportc.}: seq[TraceComponent] = @[] + proc makeTracepointComponentForExtension*(id: cstring, line: int, name: cstring, traceId: int): TraceComponent {.exportc.} = + # var tracepointComponentForExtension* {.exportc.}: TraceComponent = makeTraceComponent(data, inExtension = true) + if not tracepointComponentMapping.hasKey(name): + tracepointComponentMapping[name] = JsAssoc[int, TraceComponent]{} + if not tracepointComponentMapping[name].hasKey(line): + tracepointComponentMapping[name][line] = makeTraceComponent(data, inExtension = true, traceId = traceId) + if tracepointComponentMapping[name][line].kxi.isNil: + tracepointComponentMapping[name][line].kxi = setRenderer(proc: VNode = tracepointComponentMapping[name][line].render(), id, proc = discard) + tracepointComponentMapping[name][line].line = line + tracepointComponentMapping[name][line].name = name + return tracepointComponentMapping[name][line] + # if tracepointComponentForExtension.kxi.isNil: + # tracepointComponentForExtension.kxi = setRenderer(proc: VNode = tracepointComponentForExtension.render(), id, proc = discard) + proc calcTraceWidth(self: TraceComponent) = let editor = self.editorUI.monacoEditor let editorLayout = editor.config.layoutInfo @@ -68,11 +85,12 @@ proc showExpandValue*(self: TraceComponent, traceValue: (cstring, Value), line: self.data.redraw() method onUpdatedTable*(self: TraceComponent, response: CtUpdatedTableResponseBody) {.async.} = - if response.tableUpdate.isTrace and response.tableUpdate.data.draw == self.drawId: + if response.tableUpdate.isTrace and response.tableUpdate.data.draw == self.drawId and response.tableUpdate.traceId == self.id: self.tableCallback(response.tableUpdate.data.toJs) self.dataTable.rowsCount = response.tableUpdate.data.recordsTotal self.dataTable.updateTableRows() self.dataTable.updateTableFooter() + self.redraw() proc findTracepoint(self: TraceComponent, session: TraceSession, id: int): Tracepoint = for trace in session.tracepoints: @@ -210,17 +228,17 @@ proc runTracepoints*(data: Data) {.exportc.} = ).toJs ) -method onUpdatedTrace*(self: TraceComponent, response: TraceUpdate) {.async.} = +method onUpdatedTrace*(traceComponent: TraceComponent, response: TraceUpdate) {.async.} = # let timeInMs = now() - var traceSession = data.services.trace.traceSessions[response.sessionID] + # var traceSession = data.services.trace.traceSessions[response.sessionID] # for tracepoint in traceSession.tracepoints: - if response.updateId <= traceSession.tracepoints[^1].tracepointId and not response.refreshEventLog: - let tracepoint = self.findTracepoint(traceSession, response.updateId) - let traceComponent = - data.ui.editors[tracepoint.name].traces[tracepoint.line] + if not response.refreshEventLog: # response.updateId <= traceSession.tracepoints[^1].tracepointId and + let tracepoint = traceComponent.tracepoint # self.findTracepoint(traceSession, response.updateId) + # let traceComponent = + # data.ui.editors[tracepoint.name].traces[tracepoint.line] - if self.shouldUpdate(response): + if traceComponent.shouldUpdate(response): traceComponent.isLoading = false traceComponent.isReached = true if response.tracepointErrors.hasKey(tracepoint.tracepointId): @@ -270,7 +288,7 @@ proc createContextMenuItems(self: TraceComponent, ev: js): seq[ContextMenuItem] proc renderTableResults( self: TraceComponent, - traceSession: TraceSession + traceSession: TraceSession = nil ) = # check if there is dataTable context if self.dataTable.context.isNil: @@ -339,8 +357,16 @@ proc renderTableResults( scrollBodyDom.toJs.addEventListener(cstring"wheel", proc(ev: Event, tg: VNode) = ev.stopPropagation() self.dataTable.inputFieldChange = false - discard setTimeout(proc() = self.dataTable.updateTableRows(), 100) - discard setTimeout(proc() = self.dataTable.updateTableFooter(), 100) + discard setTimeout(proc() = + self.dataTable.updateTableRows() + self.redraw(), + 100 + ) + discard setTimeout(proc() = + self.dataTable.updateTableFooter() + self.redraw(), + 100 + ) ) proc toProgramEvent(self: TraceComponent, datatableRow: js): ProgramEvent = @@ -454,8 +480,8 @@ method refreshTrace*(self: TraceComponent) = showDomElement(self.searchInput) showDomElement(self.traceViewDom) - if self.editorUI.tabInfo.changed or self.isChanged: - self.resultsOverlayDom.children[0].innerHTML = RUN_TRACE_MESSAGE + if self.isChanged: # self.editorUI.tabInfo.changed or + # self.resultsOverlayDom.children[0].innerHTML = RUN_TRACE_MESSAGE showDomElement(self.resultsOverlayDom) return @@ -470,49 +496,32 @@ method refreshTrace*(self: TraceComponent) = else: hideDomElement(self.resultsOverlayDom) - # change button text if chart kind is changed if self.chart.changed: - self.kindSwitchButton.innerHTML = - ($self.chart.viewKind)[4..^1].toLowerAscii().cstring - - var traceSession: TraceSession - - if self.service.traceSessions.len > 0: - # take only last session: previous ones for now just stay for preserving results? - # this means disabled tracepoints dont enter in new trace sessions, but this is ok - # we dont want to show old results - for i, tracepoint in self.service.traceSessions[^1].tracepoints: - if tracepoint.name == self.name and tracepoint.line == self.line: - traceSession = self.service.traceSessions[^1] - self.indexInSession = i - - - # if it's disabled, it should continue receiving results, but only for the trace it wasnt disabled for: - # so traceSession should not be nil - if not traceSession.isNil: - # let resultsContainer = cast[Element](jq(cstring(fmt"#trace-{self.id} .trace-view"))) + # show selected chart kind + self.showSelectedChart() - if self.chart.changed: - # show selected chart kind - self.showSelectedChart() + # reset result render counter if table chart is changed + self.tracepoint.lastRender = 0 + self.chart.changed = false - # reset result render counter if table chart is changed - self.tracepoint.lastRender = 0 - self.chart.changed = false + # add results + if self.chart.viewKind == ViewTable: + if self.chartTableDom.isHidden(): + showDomElement(self.chartTableDom) - # add results - if self.chart.viewKind == ViewTable: - if self.chartTableDom.isHidden(): - showDomElement(self.chartTableDom) + self.renderTableResults() + else: + self.chart.ensure() - self.renderTableResults(traceSession) - else: - self.chart.ensure() + if self.tracepoint.tracepointError != cstring"": + self.showErrorMessage(self.tracepoint.tracepointError) + elif not self.isReached and self.isRan: + self.showEmptyResults() - if self.tracepoint.tracepointError != cstring"": - self.showErrorMessage(self.tracepoint.tracepointError) - elif not self.isReached and self.isRan: - self.showEmptyResults() + # change button text if chart kind is changed + # if self.chart.changed: + # self.kindSwitchButton.innerHTML = + # ($self.chart.viewKind)[4..^1].toLowerAscii().cstring proc saveSource*(self: TraceComponent) = self.source = self.monacoEditor.getValue() @@ -527,7 +536,7 @@ proc saveSource*(self: TraceComponent) = proc ensureEdit*(self: TraceComponent) = if self.selectorId.len == 0: - self.selectorId = j(&"edit-trace-{self.editorUI.id}-{self.line}") + self.selectorId = j(&"edit-trace-{self.id}-{self.line}") self.isChanged = true proc ensureChart(self: TraceComponent) = @@ -552,7 +561,7 @@ proc ensureChart(self: TraceComponent) = tableFooter(self.dataTable) self.chart.tableView = tableView - self.chart.id = self.data.ui.idMap[j"chart"] + # self.chart.id = self.data.ui.idMap[j"chart"] self.chart.stateID = -1 self.chart.trace = self self.chart.expression = j"trace" @@ -635,6 +644,33 @@ proc toggleHamburger(self: TraceComponent) = else: self.openHamburger() +proc getTracepointInfo(trace: TraceComponent): Tracepoint = + let code = trace.monacoEditor.getValue() + + if code != "": + trace.isRan = true + trace.isLoading = trace.isChanged + trace.forceReload = false + # trace.indexInSession = i + + trace.tracepoint.expression = code + trace.tracepoint.lastRender = 0 + trace.tracepoint.isChanged = trace.isChanged + trace.tracepoint.results = @[] + trace.tracepoint.tracepointError = cstring"" + + trace.tracepoint.name = trace.name + trace.tracepoint.line = trace.line + + result = trace.tracepoint + + trace.loggedSource = code + trace.isChanged = false + trace.isUpdating = true + trace.error = nil + + return result + proc traceMenuView(self: TraceComponent): VNode = var search = proc(ev: Event, tg: VNode) = let value = cast[cstring](ev.target.value) @@ -654,15 +690,17 @@ proc traceMenuView(self: TraceComponent): VNode = ) tdiv(class = "trace-buttons-container"): - tdiv(class = "run-trace-button", onclick = proc() = runTracepoints(self.data)): - img( - src = "public/resources/tracepoints/run_tracepoints_dark.svg", - height = "24px", - width = "24px", - class = "trace-run-button-svg" - ) + tdiv( + class = "run-trace-button", + onclick = proc() = + self.api.emit(InternalTraceMapUpdate, self.getTracepointInfo()) + self.refreshTrace() + self.api.emit(CtRunTraceSession, SourceLocation(path: self.name)) + # runTracepoints(self.data) + ): + tdiv(class = "trace-run-button-svg") tdiv(class="custom-tooltip"): - text "Run tracepoints (Ctrl-Enter)" + text "Run tracepoints (Ctrl+Enter)" switchChartKindView(self.chart) @@ -675,7 +713,7 @@ proc traceMenuView(self: TraceComponent): VNode = onblur = proc (e: Event, et: VNode) = self.closeHamburger() ): - img(src="public/resources/tracepoints/trace_hamburger_dark.svg", height="8px", width="12px", class="trace-hamburger-svg") + tdiv(class="trace-hamburger-svg") tdiv( class = "dropdown-list hidden", onmousedown = proc (ev: Event, et: VNode) = ev.preventDefault() @@ -753,9 +791,11 @@ proc ensureMonacoEditor(self: TraceComponent) = # check if trace has a monaco editor if self.monacoEditor.isNil: # get main editor monaco editor and it's theme - let activeMonacoEditor = - data.ui.editors[data.services.editor.active].monacoEditor - let activeMonacoTheme = activeMonacoEditor.getCurrentMonacoTheme() + + # TODO: Finf a way to store the monaco current theme? + # let activeMonacoEditor = + # data.ui.editors[data.services.editor.active].monacoEditor + # let activeMonacoTheme = activeMonacoEditor.getCurrentMonacoTheme() let documentTmp = domWindow.document let overflowHost = documentTmp.createElement("div") @@ -767,16 +807,17 @@ proc ensureMonacoEditor(self: TraceComponent) = jq(cstring(fmt".trace #{self.selectorId}")), MonacoEditorOptions( value: self.source, - language: toJsLang(self.editorUI.lang), - theme: activeMonacoTheme, + # language: toJsLang(self.editorUI.lang), + theme: if self.inExtension: "vs-dark" else: data.ui.editors[data.services.editor.active].monacoEditor.getCurrentMonacoTheme(), readOnly: false, automaticLayout: true, lineNumbers: traceLine, folding: false, glyphMargin: false, - fontSize: j($data.ui.fontSize) & j"px", + fontSize: j"14px", minimap: js{ enabled: false }, scrollbar: js{ + vertical: j"visible", horizontalScrollbarSize: 4, horizontalSliderSize: 4, verticalScrollbarSize: 4, @@ -794,10 +835,12 @@ proc ensureMonacoEditor(self: TraceComponent) = ) # add trace monaco editor to the register - self.data.ui.traceMonacoEditors.add(self.monacoEditor) - self.monacoEditor.onMouseWheel(proc(e: js) = - e.preventDefault() - ) + # TODO: Find a better way when using extension + if not self.inExtension: + self.data.ui.traceMonacoEditors.add(self.monacoEditor) + self.monacoEditor.onMouseWheel(proc(e: js) = + e.preventDefault() + ) # subscribe to trace monaco editor change event self.monacoEditor.onDidChangeModelContent(proc = self.error = nil @@ -808,7 +851,8 @@ proc ensureMonacoEditor(self: TraceComponent) = if self.lineCount != lineCount: self.lineCount = lineCount - self.expandWithEnter(lineCount*(data.ui.fontSize + 5)) + if not self.inExtension: + self.expandWithEnter(lineCount*(data.ui.fontSize + 5)) ) proc resizeEditorHandler(self: TraceComponent) = @@ -829,86 +873,92 @@ proc setEditorResizeObserver(self: TraceComponent) = self.resizeObserver = resizeObserver +proc traceBase(self: TraceComponent): VNode = + buildHtml(tdiv(id = cstring(fmt"trace-{self.id}"), class="trace")): + tdiv(id = cstring(fmt"trace-editor-{self.id}")) + method render*(self: TraceComponent): VNode = self.ensureEdit() self.ensureChart() - var tabInfo = self.editorUI.tabInfo + # var tabInfo = self.editorUI.tabInfo # check if main editor source has changed - if tabInfo.changed: - self.api.warnMessage(cstring("can't create a tracepoint: you have to rebuild first, file changed")) - return + # if tabInfo.changed: + # self.api.warnMessage(cstring("can't create a tracepoint: you have to rebuild first, file changed")) + # return - var initialPosition: float + # var initialPosition: float - proc resizeEditor(ev: Event, tg:VNode) = - let mouseEvent = cast[MouseEvent](ev) - let containerWidth = jq(".trace-main").offsetHeight.toJs.to(float) - let currentPosition = mouseEvent.screenY.toJs.to(float) - let movementX = (currentPosition-initialPosition) * 100 / containerWidth - let newPosition = self.editorWidth + movementX + # proc resizeEditor(ev: Event, tg:VNode) = + # let mouseEvent = cast[MouseEvent](ev) + # let containerWidth = jq(".trace-main").offsetHeight.toJs.to(float) + # let currentPosition = mouseEvent.screenY.toJs.to(float) + # let movementX = (currentPosition-initialPosition) * 100 / containerWidth + # let newPosition = self.editorWidth + movementX - if newPosition >= MIN_EDITOR_WIDTH and newPosition < MAX_EDITOR_WIDTH: - self.editorWidth = max(newPosition, MIN_EDITOR_WIDTH) + # if newPosition >= MIN_EDITOR_WIDTH and newPosition < MAX_EDITOR_WIDTH: + # self.editorWidth = max(newPosition, MIN_EDITOR_WIDTH) - jq(cstring(fmt"#trace-{self.id} .editor-textarea")).style.height = "100px" - jq(cstring(fmt"#trace-{self.id} .editor-traces")).style.height = "60px" + # jq(cstring(fmt"#trace-{self.id} .editor-textarea")).style.height = "100px" + # jq(cstring(fmt"#trace-{self.id} .editor-traces")).style.height = "60px" - initialPosition = currentPosition + # initialPosition = currentPosition var mainView = self.chart.render() result = buildHtml(tdiv): - tdiv( - class = "trace-main", - onclick = proc(ev: Event, v:VNode) = - ev.stopPropagation() - if self.data.ui.activeFocus != self: - self.data.ui.activeFocus = self, - onmousemove = proc(ev: Event, tg:VNode) = - if self.splitterClicked: - resizeEditor(ev,tg), - onmousedown = proc(ev: Event, tg: VNode) = - if self.splitterClicked: - initialPosition = cast[MouseEvent](ev).screenY.toJs.to(float), - onmouseup = proc(ev: Event, tg: VNode) = - if self.splitterClicked: - self.splitterClicked = false - ev.stopPropagation(), - onmouseleave = proc = self.splitterClicked = false - ): - tdiv(class = "trace-chevron"): - tdiv(class = "trace-chevron-arrow") - tdiv(class = "editor-info"): - tdiv( - class = cstring(fmt"editor-textarea editor-textarea-width-{$self.editorWidth}"), - style = style(StyleAttr.height, cstring(fmt"{self.lineCount * (data.ui.fontSize + 5) + 20}px")) - ): - tdiv(class = "trace-disabled-overlay tracepoint-overlay hidden"): - tdiv(class = "trace-overlay"): - text "Tracepoint is disabled" - tdiv(class = "editor-textarea-empty-header") - renderEdit(self) - tdiv(class = "editor-traces"): - traceMenuView(self) - tdiv(class = "trace-view"): - mainView - tdiv(class = "trace-results-overlay tracepoint-overlay"): - tdiv(class = "trace-overlay"): - text "Press Ctrl+Enter to run the trace." - tdiv(class = "trace-modal", id = cstring(fmt"trace-modal-window-{self.line}")): - button( - class = "modal-close-button", - onclick = proc = - document.getElementById(cstring(fmt"modal-content-{self.line}")).style.display = "none" - document.getElementById(cstring(fmt"trace-modal-window-{self.line}")).style.display = "none" - ) - tdiv(id = cstring(fmt"modal-content-{self.line}")) - -proc traceBase(self: TraceComponent): VNode = - buildHtml(tdiv(id = cstring(fmt"trace-{self.id}"), class="trace")): - tdiv(id = cstring(fmt"trace-editor-{self.id}")) + tdiv(id = cstring(fmt"trace-{self.id}"), class="trace"): + tdiv(id = cstring(fmt"trace-editor-{self.id}")) + tdiv( + class = "trace-main" + # onclick = proc(ev: Event, v:VNode) = + # ev.stopPropagation() + # if self.data.ui.activeFocus != self: + # self.data.ui.activeFocus = self, + # onmousemove = proc(ev: Event, tg:VNode) = + # if self.splitterClicked: + # resizeEditor(ev,tg), + # onmousedown = proc(ev: Event, tg: VNode) = + # if self.splitterClicked: + # initialPosition = cast[MouseEvent](ev).screenY.toJs.to(float), + # onmouseup = proc(ev: Event, tg: VNode) = + # if self.splitterClicked: + # self.splitterClicked = false + # ev.stopPropagation(), + # onmouseleave = proc = self.splitterClicked = false + ): + tdiv(class = "trace-chevron"): + tdiv(class = "trace-chevron-arrow") + tdiv(class = "editor-info"): + tdiv( + class = cstring(fmt"editor-textarea editor-textarea-width-{$self.editorWidth}"), + style = + if not self.inExtension: + style(StyleAttr.height, cstring(fmt"{self.lineCount * (data.ui.fontSize + 5) + 20}px")) + else: + style(StyleAttr.height, cstring(fmt"150px")) + ): + tdiv(class = "trace-disabled-overlay tracepoint-overlay hidden"): + tdiv(class = "trace-overlay"): + text "Tracepoint is disabled" + tdiv(class = "editor-textarea-empty-header") + renderEdit(self) + tdiv(class = "editor-traces"): + traceMenuView(self) + tdiv(class = "trace-view"): + mainView + tdiv(class = "trace-results-overlay tracepoint-overlay"): + tdiv(class = "trace-overlay"): + text "Press Ctrl+Enter to run the trace." + tdiv(class = "trace-modal", id = cstring(fmt"trace-modal-window-{self.line}")): + button( + class = "modal-close-button", + onclick = proc = + document.getElementById(cstring(fmt"modal-content-{self.line}")).style.display = "none" + document.getElementById(cstring(fmt"trace-modal-window-{self.line}")).style.display = "none" + ) + tdiv(id = cstring(fmt"modal-content-{self.line}")) const TOGGLE_REPEAT_TIME_LIMIT = 100i64 @@ -935,6 +985,105 @@ proc resizeTraceHandler(self: TraceComponent) = self.dataTable.resizeTable() self.dataTable.updateTableFooter() +proc togglePoint*(trace: TraceComponent) = + if trace.viewZone.isNil: + # create trace base dom Node + let traceNode = vnodeToDom(traceBase(trace), kxi) + + # config new view zone in monaco editor + trace.viewZone = js{ + afterLineNumber: line, + heightInPx: 90, + domNode: traceNode + } + + # add configured zone + # tabInfo.monacoEditor.changeViewZones do (view: js): + # trace.zoneId = cast[int](view.addZone(trace.viewZone)) + + # # add tracepoint to points register + # data.pointList.tracepoints[trace.tracepoint.tracepointId] = trace.tracepoint + + # render current trace component + # trace.viewZone.domNode.appendChild(vnodeToDom(trace.render(), KaraxInstance())) + + # sаve references to component dom elements + + trace.searchInput = + cast[Element](jq( + cstring(&"#trace-{trace.id} .trace-search") + )) + trace.traceViewDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .trace-view") + )) + trace.hamburgerButton = + cast[Element](jq( + cstring(&"#trace-{trace.id} .hamburger-dropdown") + )) + trace.hamburgerDropdownList = + cast[Element](jq( + cstring(&"#trace-{trace.id} .hamburger-dropdown-container .dropdown-list") + )) + trace.overlayDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .trace-disabled-overlay") + )) + trace.resultsOverlayDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .trace-results-overlay") + )) + trace.kindSwitchButton = + cast[Element](jq( + cstring(&"#trace-{trace.id} .select-view-kind-button") + )) + trace.kindSwitchDropDownList = + cast[Element](jq( + cstring(&"#trace-{trace.id} .kind-dropdown-menu") + )) + trace.chartTableDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .chart-table") + )) + + # add reference to trace table footer dom + trace.dataTable.footerDom = + cast[Element](trace.chartTableDom.findNodeInElement(".data-tables-footer")) + + trace.chartLineDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .chart-line") + )) + + trace.chartPieDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .chart-pie") + )) + + trace.runTraceButtonDom = + cast[Element](jq( + cstring(&"#trace-{trace.id} .run-trace-button") + )) + + trace.kindSwitchButton.toJs.parentNode.addEventListener(cstring("click"), proc = + trace.toggleChartKindMenu() + trace.refreshTrace() + ) + + trace.kindSwitchButton.toJs.parentNode.addEventListener(cstring("blur"), proc = + trace.closeKindSwitchMenu() + ) + + # create resize observer + if trace.resizeObserver.isNil: + trace.setEditorResizeObserver() + + # create monaco editor if there is not any yet + trace.ensureMonacoEditor() + + # set trace to expanded + trace.expanded = true + proc toggleTrace*(editorUI: EditorViewComponent, name: cstring, line: int) = let newTime = now() @@ -1093,7 +1242,8 @@ proc toggleTrace*(editorUI: EditorViewComponent, name: cstring, line: int) = method onCompleteMove*(self: TraceComponent, response: MoveState) {.async.} = self.location = response.location - self.editorUI.updateLineNumbersOnly() + # TODO: For now find a better way when in extension + # self.editorUI.updateLineNumbersOnly() method onError*(self: TraceComponent, error: DebuggerError) {.async.} = if error.kind == ErrorTracepoint: @@ -1104,6 +1254,8 @@ method onError*(self: TraceComponent, error: DebuggerError) {.async.} = method register*(self: TraceComponent, api: MediatorWithSubscribers) = self.api = api api.subscribe(CtCompleteMove, proc(kind: CtEventKind, response: MoveState, sub: Subscriber) = + self.ensureMonacoEditor() + self.togglePoint() discard self.onCompleteMove(response) ) api.subscribe(CtUpdatedTable, proc(kind: CtEventKind, response: CtUpdatedTableResponseBody, sub: Subscriber) = @@ -1112,9 +1264,7 @@ method register*(self: TraceComponent, api: MediatorWithSubscribers) = api.subscribe(CtUpdatedTrace, proc(kind: CtEventKind, response: TraceUpdate, sub: Subscriber) = discard self.onUpdatedTrace(response) ) - -proc registerTraceComponent*(component: TraceComponent, api: MediatorWithSubscribers) {.exportc.} = - component.register(api) + api.emit(InternalLastCompleteMove, EmptyArg()) proc closeTrace*(self: TraceComponent) = if self.isRan: @@ -1149,3 +1299,5 @@ proc closeTrace*(self: TraceComponent) = self.data.redraw() +proc registerTracepointComponent*(component: TraceComponent, api: MediatorWithSubscribers) {.exportc.} = + component.register(api) \ No newline at end of file diff --git a/src/frontend/utils.nim b/src/frontend/utils.nim index f81560c84..9fbe981f1 100644 --- a/src/frontend/utils.nim +++ b/src/frontend/utils.nim @@ -523,28 +523,80 @@ proc makeChartComponent*(data:Data): ChartComponent = pieLabels: @[], pieValues: @[]) -proc makeTraceComponent*(data: Data, editorUI: EditorViewComponent, name: cstring, line: int): TraceComponent = - let offset = - if editorUI.lang == LangAsm: - editorUI.tabInfo.instructions.instructions[line - 1].offset - else: - NO_OFFSET - let id = data.services.trace.tracePID - data.services.trace.tracePID += 1 +proc makeFlowComponent*(data: Data, position: int, inExtension: bool = false): FlowComponent = + FlowComponent( + # api: self.api, + # id: self.id, + flow: nil, + # tab: self.tabInfo, + # location: location, + inExtension: inExtension, + multilineZones: JsAssoc[int, MultilineZone]{}, + flowDom: JsAssoc[int, Node]{}, + shouldRecalcFlow: false, + flowLoops: JsAssoc[int, FlowLoop]{}, + flowLines: JsAssoc[int, FlowLine]{}, + activeStep: FlowStep(rrTicks: -1), + selectedLine: -1, + selectedLineInGroup: -1, + selectedStepCount: -1, + lineHeight: 20, + # multilineFlowLines: multilineFlowLines(), + multilineValuesDoms: JsAssoc[int, JsAssoc[cstring, Node]]{}, + loopLineSteps: JsAssoc[int, int]{}, + inlineDecorations: JsAssoc[int, InlineDecorations]{}, + # editorUI: self, + # scratchpadUI: if self.data.ui.componentMapping[Content.Scratchpad].len > 0: self.data.scratchpadComponent(0) else: nil, + # editor: self.service, + # service: self.data.services.flow, + # data: self.data, + lineGroups: JsAssoc[int, Group]{}, + status: FlowUpdateState(kind: FlowWaitingForStart), + statusWidget: nil, + sliderWidgets: JsAssoc[int, js]{}, + lineWidgets: JsAssoc[int, js]{}, + multilineWidgets: JsAssoc[int, JsAssoc[cstring, js]]{}, + stepNodes: JsAssoc[int, kdom.Node]{}, + loopStates: JsAssoc[int, LoopState]{}, + viewZones: JsAssoc[int, int]{}, + loopViewZones: JsAssoc[int, int]{}, + loopColumnMinWidth: 15, + shrinkedLoopColumnMinWidth: 8, + pixelsPerSymbol: 8, + distanceBetweenValues: 10, + distanceToSource: 50, + inlineValueWidth: 80, + bufferMaxOffsetInPx: 300, + maxWidth: 0, + modalValueComponent: JsAssoc[cstring, ValueComponent]{}, + valueMode: BeforeValueMode, + position: position + ) + +proc makeTraceComponent*(data: Data, editorUI: EditorViewComponent = nil, name: cstring = "", line: int = 0, inExtension: bool = false, traceId: int = 0): TraceComponent = # editorUI: EditorViewComponent, name: cstring, + # let offset = + # if editorUI.lang == LangAsm: + # editorUI.tabInfo.instructions.instructions[line - 1].offset + # else: + # NO_OFFSET + # let traceId = 0 # data.services.trace.tracePID + # data.services.trace.tracePID += 1 + + let id = if inExtension: traceId else: data.services.trace.tracePID result = TraceComponent( id: id, lineCount: 1, resultsHeight: 36, - name: name, + # name: name, line: line, tracepoint: Tracepoint( tracepointId: id, mode: TracInlineCode, name: name, line: line, - offset: offset, - lang: editorUI.lang, + # offset: offset, + # lang: editorUI.lang, # TODO: Fix this after extension implementation expression: j"", lastRender: 0, results: @[], @@ -554,10 +606,15 @@ proc makeTraceComponent*(data: Data, editorUI: EditorViewComponent, name: cstrin service: data.services.trace, editorWidth: 50, traceHeight: 210, + inExtension: inExtension, dataTable: DataTableComponent(rowHeight: 30, inputFieldChange: false)) - result.chart.setId(data.ui.idMap["chart"]) - data.ui.idMap["chart"] = data.ui.idMap["chart"] + 1 - data.ui.editors[name].traces[line] = result + + if not inExtension: + result.chart.setId(data.ui.idMap["chart"]) + data.ui.idMap["chart"] = data.ui.idMap["chart"] + 1 + data.ui.editors[name].traces[line] = result + else: + result.chart.setId(id) data.registerComponent(result, Content.Trace) proc makeMenuComponent*(data: Data): MenuComponent = diff --git a/src/frontend/vscode.nim b/src/frontend/vscode.nim index a44558e1d..232ae731c 100644 --- a/src/frontend/vscode.nim +++ b/src/frontend/vscode.nim @@ -171,6 +171,9 @@ when defined(ctInExtension): return parseInt(traceNumber.strip()) return NO_INDEX + proc getFlowList*() {.async, exportc.}= + discard + proc getCurrentTrace*(codetracerExe: cstring, workDir: cstring, isNixOS: bool): Future[JsObject] {.async, exportc.} = let outputResult = await readCTOutput( codetracerExe,