mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 02:01:47 +03:00
Show diagnostics in Code Editor (#8375)
As suggested by @JaroslavTulach. - Shows diagnostics sent by `executionContext/executionStatus` - Shows `Panic`s and `DataflowError`s sent by `executionContext/expressionUpdates` # Important Notes None
This commit is contained in:
parent
a9099ddce5
commit
87dfb57f53
@ -3,8 +3,9 @@ import { useGraphStore } from '@/stores/graph'
|
||||
import { useProjectStore } from '@/stores/project'
|
||||
import { useSuggestionDbStore } from '@/stores/suggestionDatabase'
|
||||
import { useAutoBlur } from '@/util/autoBlur'
|
||||
import type { Highlighter } from '@/util/codemirror'
|
||||
import type { Diagnostic, Highlighter } from '@/util/codemirror'
|
||||
import { usePointer } from '@/util/events'
|
||||
import { chain } from '@/util/iterable'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import { rangeEncloses, type ExprId } from 'shared/yjsModel'
|
||||
import { computed, onMounted, ref, watchEffect } from 'vue'
|
||||
@ -15,6 +16,7 @@ import { unwrap } from '../util/result'
|
||||
const {
|
||||
bracketMatching,
|
||||
foldGutter,
|
||||
lintGutter,
|
||||
highlightSelectionMatches,
|
||||
minimalSetup,
|
||||
EditorState,
|
||||
@ -24,6 +26,8 @@ const {
|
||||
defaultHighlightStyle,
|
||||
tooltips,
|
||||
enso,
|
||||
linter,
|
||||
lsDiagnosticsToCMDiagnostics,
|
||||
hoverTooltip,
|
||||
} = await import('@/util/codemirror')
|
||||
|
||||
@ -33,6 +37,41 @@ const suggestionDbStore = useSuggestionDbStore()
|
||||
const rootElement = ref<HTMLElement>()
|
||||
useAutoBlur(rootElement)
|
||||
|
||||
const executionContextDiagnostics = computed(() =>
|
||||
projectStore.module
|
||||
? lsDiagnosticsToCMDiagnostics(
|
||||
projectStore.module.doc.contents.toString(),
|
||||
projectStore.diagnostics,
|
||||
)
|
||||
: [],
|
||||
)
|
||||
|
||||
const expressionUpdatesDiagnostics = computed(() => {
|
||||
const nodeMap = graphStore.db.nodeIdToNode
|
||||
const updates = projectStore.computedValueRegistry.db
|
||||
const panics = updates.type.reverseLookup('Panic')
|
||||
const errors = updates.type.reverseLookup('DataflowError')
|
||||
const diagnostics: Diagnostic[] = []
|
||||
for (const id of chain(panics, errors)) {
|
||||
const update = updates.get(id)
|
||||
if (!update) continue
|
||||
const node = nodeMap.get(id)
|
||||
if (!node) continue
|
||||
const [from, to] = node.rootSpan.span()
|
||||
switch (update.payload.type) {
|
||||
case 'Panic': {
|
||||
diagnostics.push({ from, to, message: update.payload.message, severity: 'error' })
|
||||
break
|
||||
}
|
||||
case 'DataflowError': {
|
||||
diagnostics.push({ from, to, message: 'Unknown data flow error', severity: 'error' })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return diagnostics
|
||||
})
|
||||
|
||||
// == CodeMirror editor setup ==
|
||||
|
||||
const editorView = new EditorView()
|
||||
@ -51,6 +90,7 @@ watchEffect(() => {
|
||||
syntaxHighlighting(defaultHighlightStyle as Highlighter),
|
||||
bracketMatching(),
|
||||
foldGutter(),
|
||||
lintGutter(),
|
||||
highlightSelectionMatches(),
|
||||
tooltips({ position: 'absolute' }),
|
||||
hoverTooltip((ast, syn) => {
|
||||
@ -100,6 +140,7 @@ watchEffect(() => {
|
||||
return { dom }
|
||||
}),
|
||||
enso(),
|
||||
linter(() => [...executionContextDiagnostics.value, ...expressionUpdatesDiagnostics.value]),
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
@ -525,6 +525,11 @@ export const useProjectStore = defineStore('project', () => {
|
||||
const computedValueRegistry = ComputedValueRegistry.WithExecutionContext(executionContext)
|
||||
const visualizationDataRegistry = new VisualizationDataRegistry(executionContext, dataConnection)
|
||||
|
||||
const diagnostics = ref<Diagnostic[]>([])
|
||||
executionContext.on('executionStatus', (newDiagnostics) => {
|
||||
diagnostics.value = newDiagnostics
|
||||
})
|
||||
|
||||
function useVisualizationData(
|
||||
configuration: WatchSource<Opt<NodeVisualizationConfiguration>>,
|
||||
): ShallowRef<{} | undefined> {
|
||||
@ -561,6 +566,7 @@ export const useProjectStore = defineStore('project', () => {
|
||||
},
|
||||
name: projectName,
|
||||
executionContext,
|
||||
diagnostics,
|
||||
module,
|
||||
modulePath,
|
||||
projectModel,
|
||||
|
@ -11,6 +11,7 @@ export {
|
||||
foldNodeProp,
|
||||
syntaxHighlighting,
|
||||
} from '@codemirror/language'
|
||||
export { lintGutter, linter, type Diagnostic } from '@codemirror/lint'
|
||||
export { highlightSelectionMatches } from '@codemirror/search'
|
||||
export { EditorState } from '@codemirror/state'
|
||||
export { EditorView, tooltips, type TooltipView } from '@codemirror/view'
|
||||
@ -26,6 +27,7 @@ import {
|
||||
languageDataProp,
|
||||
syntaxTree,
|
||||
} from '@codemirror/language'
|
||||
import { type Diagnostic } from '@codemirror/lint'
|
||||
import { hoverTooltip as originalHoverTooltip, type TooltipView } from '@codemirror/view'
|
||||
import {
|
||||
NodeProp,
|
||||
@ -39,6 +41,34 @@ import {
|
||||
} from '@lezer/common'
|
||||
import { styleTags, tags } from '@lezer/highlight'
|
||||
import { EditorView } from 'codemirror'
|
||||
import type { Diagnostic as LSDiagnostic } from 'shared/languageServerTypes'
|
||||
|
||||
export function lsDiagnosticsToCMDiagnostics(
|
||||
source: string,
|
||||
diagnostics: LSDiagnostic[],
|
||||
): Diagnostic[] {
|
||||
if (!diagnostics.length) return []
|
||||
const results: Diagnostic[] = []
|
||||
let pos = 0
|
||||
const lineStartIndices = []
|
||||
for (const line of source.split('\n')) {
|
||||
lineStartIndices.push(pos)
|
||||
pos += line.length + 1
|
||||
}
|
||||
for (const diagnostic of diagnostics) {
|
||||
if (!diagnostic.location) continue
|
||||
results.push({
|
||||
from:
|
||||
(lineStartIndices[diagnostic.location.start.line] ?? 0) +
|
||||
diagnostic.location.start.character,
|
||||
to: (lineStartIndices[diagnostic.location.end.line] ?? 0) + diagnostic.location.end.character,
|
||||
message: diagnostic.message,
|
||||
severity:
|
||||
diagnostic.kind === 'Error' ? 'error' : diagnostic.kind === 'Warning' ? 'warning' : 'info',
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
type AstNode = AstExtended<Ast.Tree | Ast.Token, false>
|
||||
|
||||
|
@ -7,7 +7,7 @@ import type {
|
||||
ProfilingInfo,
|
||||
} from 'shared/languageServerTypes'
|
||||
import { markRaw } from 'vue'
|
||||
import { ReactiveDb } from './database/reactiveDb'
|
||||
import { ReactiveDb, ReactiveIndex } from './database/reactiveDb'
|
||||
|
||||
export interface ExpressionInfo {
|
||||
typename: string | undefined
|
||||
@ -16,9 +16,13 @@ export interface ExpressionInfo {
|
||||
profilingInfo: ProfilingInfo[]
|
||||
}
|
||||
|
||||
class ComputedValueDb extends ReactiveDb<ExpressionId, ExpressionInfo> {
|
||||
type = new ReactiveIndex(this, (id, info) => [[id, info.payload.type]])
|
||||
}
|
||||
|
||||
/** This class holds the computed values that have been received from the language server. */
|
||||
export class ComputedValueRegistry {
|
||||
public db: ReactiveDb<ExpressionId, ExpressionInfo> = new ReactiveDb()
|
||||
public db = new ComputedValueDb()
|
||||
private _updateHandler = this.processUpdates.bind(this)
|
||||
private executionContext: ExecutionContext | undefined
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user