Files
tiptap/docs/src/demos/Experiments/Linter/extension/Linter.ts
2021-01-19 18:01:25 +01:00

99 lines
2.3 KiB
TypeScript

// @ts-nocheck
import { Extension } from '@tiptap/core'
import { Decoration, DecorationSet } from 'prosemirror-view'
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
function renderIcon(issue) {
const icon = document.createElement('div')
icon.className = 'lint-icon'
icon.title = issue.message
icon.issue = issue
return icon
}
function runAllLinterPlugins(doc, plugins) {
const decorations: [any?] = []
const results = plugins.map(LinterPlugin => {
return new LinterPlugin(doc).scan().getResults()
}).flat()
results.forEach(issue => {
decorations.push(Decoration.inline(issue.from, issue.to, {
class: 'problem',
}),
Decoration.widget(issue.from, renderIcon(issue)))
})
return DecorationSet.create(doc, decorations)
}
export interface LinterOptions {
plugins: [any],
}
export const Linter = Extension.create({
name: 'linter',
defaultOptions: <LinterOptions>{
plugins: [],
},
addProseMirrorPlugins() {
const { plugins } = this.options
return [
new Plugin({
key: new PluginKey('linter'),
state: {
init(_, { doc }) {
return runAllLinterPlugins(doc, plugins)
},
apply(transaction, oldState) {
return transaction.docChanged
? runAllLinterPlugins(transaction.doc, plugins)
: oldState
},
},
props: {
decorations(state) {
return this.getState(state)
},
handleClick(view, _, event) {
if (/lint-icon/.test(event.target.className)) {
const { from, to } = event.target.issue
view.dispatch(
view.state.tr
.setSelection(TextSelection.create(view.state.doc, from, to))
.scrollIntoView(),
)
return true
}
},
handleDoubleClick(view, _, event) {
if (/lint-icon/.test(event.target.className)) {
const prob = event.target.issue
if (prob.fix) {
prob.fix(view)
view.focus()
return true
}
}
},
},
}),
]
},
})
declare module '@tiptap/core' {
interface AllExtensions {
Linter: typeof Linter,
}
}