mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 11:41:56 +03:00
b7a8909818
- Improved performance by batching simulatenous node edits, including metadata updates when dragging many selected nodes together. - Updated Vue to new version, allowing us to use `defineModel`. - Fixed #9161 - Unified all handling of auto-blur by making `useAutoBlur` cheap to register - all logic goes through a single window event handler. - Combined all `ResizeObserver`s into one. - Fixed the behaviour of repeated toast messages. Now only the latest compilation status is visible at any given time, and the errors disappear once compilation passes. - Actually fixed broken interaction of node and visualization widths. There no longer is a style feedback loop and the visible node backdrop width no longer jumps or randomly fails to update.
95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
import { print, type AstId, type Module, type ModuleUpdate } from '.'
|
|
import { assertDefined } from '../util/assert'
|
|
import type { SourceRangeEdit } from '../util/data/text'
|
|
import { offsetEdit, textChangeToEdits } from '../util/data/text'
|
|
import type { Origin, SourceRange } from '../yjsModel'
|
|
import { rangeEquals, sourceRangeFromKey } from '../yjsModel'
|
|
|
|
/** Provides a view of the text representation of a module,
|
|
* and information about the correspondence between the text and the ASTs,
|
|
* that can be kept up-to-date by applying AST changes.
|
|
*/
|
|
export class SourceDocument {
|
|
private text_: string
|
|
private readonly spans: Map<AstId, SourceRange>
|
|
private readonly observers: SourceDocumentObserver[]
|
|
|
|
private constructor(text: string, spans: Map<AstId, SourceRange>) {
|
|
this.text_ = text
|
|
this.spans = spans
|
|
this.observers = []
|
|
}
|
|
|
|
static Empty() {
|
|
return new this('', new Map())
|
|
}
|
|
|
|
clear() {
|
|
if (this.spans.size !== 0) this.spans.clear()
|
|
if (this.text_ !== '') {
|
|
const range: SourceRange = [0, this.text_.length]
|
|
this.text_ = ''
|
|
this.notifyObservers([{ range, insert: '' }], undefined)
|
|
}
|
|
}
|
|
|
|
applyUpdate(module: Module, update: ModuleUpdate) {
|
|
for (const id of update.nodesDeleted) this.spans.delete(id)
|
|
const root = module.root()
|
|
if (!root) return
|
|
const subtreeTextEdits = new Array<SourceRangeEdit>()
|
|
const printed = print(root)
|
|
for (const [key, nodes] of printed.info.nodes) {
|
|
const range = sourceRangeFromKey(key)
|
|
for (const node of nodes) {
|
|
const oldSpan = this.spans.get(node.id)
|
|
if (!oldSpan || !rangeEquals(range, oldSpan)) this.spans.set(node.id, range)
|
|
if (update.updateRoots.has(node.id) && node.id !== root.id) {
|
|
assertDefined(oldSpan)
|
|
const oldCode = this.text_.slice(oldSpan[0], oldSpan[1])
|
|
const newCode = printed.code.slice(range[0], range[1])
|
|
const subedits = textChangeToEdits(oldCode, newCode).map((textEdit) =>
|
|
offsetEdit(textEdit, oldSpan[0]),
|
|
)
|
|
subtreeTextEdits.push(...subedits)
|
|
}
|
|
}
|
|
}
|
|
if (printed.code !== this.text_) {
|
|
const textEdits =
|
|
update.updateRoots.has(root.id) ?
|
|
[{ range: [0, this.text_.length] satisfies SourceRange, insert: printed.code }]
|
|
: subtreeTextEdits
|
|
this.text_ = printed.code
|
|
this.notifyObservers(textEdits, update.origin)
|
|
}
|
|
}
|
|
|
|
get text(): string {
|
|
return this.text_
|
|
}
|
|
|
|
getSpan(id: AstId): SourceRange | undefined {
|
|
return this.spans.get(id)
|
|
}
|
|
|
|
observe(observer: SourceDocumentObserver) {
|
|
this.observers.push(observer)
|
|
if (this.text_.length) observer([{ range: [0, 0], insert: this.text_ }], undefined)
|
|
}
|
|
|
|
unobserve(observer: SourceDocumentObserver) {
|
|
const index = this.observers.indexOf(observer)
|
|
if (index !== undefined) this.observers.splice(index, 1)
|
|
}
|
|
|
|
private notifyObservers(textEdits: SourceRangeEdit[], origin: Origin | undefined) {
|
|
for (const o of this.observers) o(textEdits, origin)
|
|
}
|
|
}
|
|
|
|
export type SourceDocumentObserver = (
|
|
textEdits: SourceRangeEdit[],
|
|
origin: Origin | undefined,
|
|
) => void
|