Merge pull request #1492 from sereneinserenade/main

Adding types to Linter and making the structure a bit easier
This commit is contained in:
Hans Pagel
2021-07-20 13:54:24 +02:00
committed by GitHub
6 changed files with 50 additions and 32 deletions

View File

@@ -1,10 +1,15 @@
// @ts-nocheck
import { Extension } from '@tiptap/core'
import { Decoration, DecorationSet } from 'prosemirror-view'
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
import { Node as ProsemirrorNode } from 'prosemirror-model'
import LinterPlugin, { Result as Issue } from './LinterPlugin'
function renderIcon(issue) {
const icon = document.createElement('div')
interface IconDivElement extends HTMLDivElement {
issue?: Issue
}
function renderIcon(issue: Issue) {
const icon: IconDivElement = document.createElement('div')
icon.className = 'lint-icon'
icon.title = issue.message
@@ -13,11 +18,11 @@ function renderIcon(issue) {
return icon
}
function runAllLinterPlugins(doc, plugins) {
function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterPlugin>) {
const decorations: [any?] = []
const results = plugins.map(LinterPlugin => {
return new LinterPlugin(doc).scan().getResults()
const results = plugins.map(RegisteredLinterPlugin => {
return new RegisteredLinterPlugin(doc).scan().getResults()
}).flat()
results.forEach(issue => {
@@ -31,7 +36,7 @@ function runAllLinterPlugins(doc, plugins) {
}
export interface LinterOptions {
plugins: [any],
plugins: Array<typeof LinterPlugin>,
}
export const Linter = Extension.create({
@@ -62,8 +67,9 @@ export const Linter = Extension.create({
return this.getState(state)
},
handleClick(view, _, event) {
if (/lint-icon/.test(event.target.className)) {
const { from, to } = event.target.issue
const target = (event.target as IconDivElement)
if (/lint-icon/.test(target.className) && target.issue) {
const { from, to } = target.issue
view.dispatch(
view.state.tr
@@ -73,17 +79,22 @@ export const Linter = Extension.create({
return true
}
return false
},
handleDoubleClick(view, _, event) {
if (/lint-icon/.test(event.target.className)) {
const prob = event.target.issue
const target = (event.target as IconDivElement)
if (/lint-icon/.test((event.target as HTMLElement).className) && target.issue) {
const prob = target.issue
if (prob.fix) {
prob.fix(view)
prob.fix(view, prob)
view.focus()
return true
}
}
return false
},
},
}),

View File

@@ -1,8 +1,10 @@
interface Result {
import { Node as ProsemirrorNode } from 'prosemirror-model'
export interface Result {
message: string,
from: number,
to: number,
fix?: null
fix?: Function
}
export default class LinterPlugin {
@@ -10,11 +12,11 @@ export default class LinterPlugin {
private results: Array<Result> = []
constructor(doc: any) {
constructor(doc: ProsemirrorNode) {
this.doc = doc
}
record(message: string, from: number, to: number, fix?: null) {
record(message: string, from: number, to: number, fix?: Function) {
this.results.push({
message,
from,
@@ -23,6 +25,10 @@ export default class LinterPlugin {
})
}
scan() {
return this
}
getResults() {
return this.results
}

View File

@@ -1,4 +1,3 @@
// @ts-nocheck
import LinterPlugin from '../LinterPlugin'
export class BadWords extends LinterPlugin {
@@ -6,7 +5,7 @@ export class BadWords extends LinterPlugin {
public regex = /\b(obviously|clearly|evidently|simply)\b/ig
scan() {
this.doc.descendants((node: any, position: any) => {
this.doc.descendants((node: any, position: number) => {
if (!node.isText) {
return
}

View File

@@ -1,15 +1,15 @@
// @ts-nocheck
import LinterPlugin from '../LinterPlugin'
import { EditorView } from 'prosemirror-view'
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
export class HeadingLevel extends LinterPlugin {
fixHeader(level) {
return function ({ state, dispatch }) {
dispatch(state.tr.setNodeMarkup(this.from - 1, null, { level }))
fixHeader(level: number) {
return function ({ state, dispatch }: EditorView, issue: Issue) {
dispatch(state.tr.setNodeMarkup(issue.from - 1, undefined, { level }))
}
}
scan() {
let lastHeadLevel = null
let lastHeadLevel: number | null = null
this.doc.descendants((node, position) => {
if (node.type.name === 'heading') {

View File

@@ -1,14 +1,14 @@
// @ts-nocheck
import LinterPlugin from '../LinterPlugin'
import { EditorView } from 'prosemirror-view'
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
export class Punctuation extends LinterPlugin {
public regex = / ([,.!?:]) ?/g
fix(replacement: any) {
return function ({ state, dispatch }) {
return function ({ state, dispatch }: EditorView, issue: Issue) {
dispatch(
state.tr.replaceWith(
this.from, this.to,
issue.from, issue.to,
state.schema.text(replacement),
),
)
@@ -17,9 +17,9 @@ export class Punctuation extends LinterPlugin {
scan() {
this.doc.descendants((node, position) => {
if (!node.isText) {
return
}
if (!node.isText) return
if (!node.text) return
const matches = this.regex.exec(node.text)

View File

@@ -1,6 +1,8 @@
# Linter
⚠️ Experiment
⚠️ Experiment, currently not supported or maintained
Linter can be used to check the content as per your wish and highlight it to the user. Linter extension can have multiple plugins for each task you want to achieve.
## Issues
* There is no decoration API in tiptap, thats why this is a lot of ProseMirror work. Before well publish that example, wed need to find a few ways to make it more tiptap-like. For example, it would be great to use Vue/React components for the widget.