Browse Source

feat: add linting cm6

Andrew Bastin 4 years ago
parent
commit
8f166b8b3f

+ 63 - 18
packages/hoppscotch-app/helpers/editor/codemirror.ts

@@ -30,16 +30,20 @@ import { watch, onMounted, ref, Ref, useContext } from "@nuxtjs/composition-api"
 import {
   EditorState,
   Compartment,
-  StateField,
   EditorSelection,
   TransactionSpec,
+  Extension,
 } from "@codemirror/state"
 import { EditorView, keymap, ViewPlugin, ViewUpdate } from "@codemirror/view"
 import { defaultKeymap } from "@codemirror/commands"
 import { basicSetup } from "@codemirror/basic-setup"
-import { javascript } from "@codemirror/lang-javascript"
-import { json } from "@codemirror/lang-json"
+import { javascriptLanguage } from "@codemirror/lang-javascript"
+import { Language, LanguageSupport } from "@codemirror/language"
+import { linter } from "@codemirror/lint"
+import { jsonLanguage } from "@codemirror/lang-json"
 import { onBeforeUnmount } from "@vue/runtime-dom"
+import { pipe } from "fp-ts/function"
+import * as O from "fp-ts/Option"
 import { isJSONContentType } from "../utils/contenttypes"
 import { Completer } from "./completion"
 import { LinterDefinition } from "./linting/linter"
@@ -229,21 +233,56 @@ export function useCodemirror(
   }
 }
 
-const getEditorLanguage = (mode: string) => {
-  if (isJSONContentType(mode)) {
-    return json()
-  } else if (mode === "application/javascript") {
-    return javascript()
-  } else {
-    return StateField.define({
-      create() {
-        return null
-      },
-      update() {},
+const hoppLinterExt = (hoppLinter: LinterDefinition): Extension => {
+  return linter(async (view) => {
+    // Requires full document scan, hence expensive on big files, force disable on big files ?
+    const linterResult = await hoppLinter(
+      view.state.doc.toJSON().join(view.state.lineBreak)
+    )
+
+    return linterResult.map((result) => {
+      const startPos =
+        view.state.doc.line(result.from.line + 1).from + result.from.ch
+      const endPos = view.state.doc.line(result.to.line + 1).from + result.to.ch
+
+      return {
+        from: startPos,
+        to: endPos,
+        message: result.message,
+        severity: result.severity,
+      }
     })
+  })
+}
+
+const hoppLang = (
+  language: Language,
+  linter?: LinterDefinition | undefined
+) => {
+  return new LanguageSupport(language, linter ? [hoppLinterExt(linter)] : [])
+}
+
+const getLanguage = (langMime: string): Language | null => {
+  if (isJSONContentType(langMime)) {
+    return jsonLanguage
+  } else if (langMime === "application/javascript") {
+    return javascriptLanguage
   }
+
+  // None matched, so return null
+  return null
 }
 
+const getEditorLanguage = (
+  langMime: string,
+  linter: LinterDefinition | undefined
+): Extension =>
+  pipe(
+    O.fromNullable(getLanguage(langMime)),
+    O.map((lang) => hoppLang(lang, linter)),
+    O.getOrElseW(() => [])
+  )
+
 export function useNewCodemirror(
   el: Ref<any | null>,
   value: Ref<string>,
@@ -297,7 +336,10 @@ export function useNewCodemirror(
       ),
       EditorState.changeFilter.of(() => !options.extendedEditorConfig.readOnly),
       language.of(
-        getEditorLanguage((options.extendedEditorConfig.mode as any) ?? "")
+        getEditorLanguage(
+          (options.extendedEditorConfig.mode as any) ?? "",
+          options.linter ?? undefined
+        )
       ),
       lineWrapping.of(
         options.extendedEditorConfig.lineWrapping
@@ -354,11 +396,14 @@ export function useNewCodemirror(
   )
 
   watch(
-    () => options.extendedEditorConfig.mode,
-    (newMode) => {
+    () => [options.extendedEditorConfig.mode, options.linter],
+    () => {
       dispatch({
         effects: language.reconfigure(
-          getEditorLanguage((newMode as any) ?? "")
+          getEditorLanguage(
+            (options.extendedEditorConfig.mode as any) ?? "",
+            options.linter ?? undefined
+          )
         ),
       })
     }

+ 1 - 0
packages/hoppscotch-app/package.json

@@ -40,6 +40,7 @@
     "@codemirror/lang-javascript": "^0.19.2",
     "@codemirror/lang-json": "^0.19.1",
     "@codemirror/language": "^0.19.3",
+    "@codemirror/lint": "^0.19.2",
     "@codemirror/state": "^0.19.3",
     "@codemirror/text": "^0.19.5",
     "@codemirror/view": "^0.19.12",

+ 8 - 6
pnpm-lock.yaml

@@ -26,6 +26,7 @@ importers:
       '@codemirror/lang-javascript': ^0.19.2
       '@codemirror/lang-json': ^0.19.1
       '@codemirror/language': ^0.19.3
+      '@codemirror/lint': ^0.19.2
       '@codemirror/state': ^0.19.3
       '@codemirror/text': ^0.19.5
       '@codemirror/view': ^0.19.12
@@ -140,6 +141,7 @@ importers:
       '@codemirror/lang-javascript': 0.19.2
       '@codemirror/lang-json': 0.19.1
       '@codemirror/language': 0.19.3
+      '@codemirror/lint': 0.19.2
       '@codemirror/state': 0.19.3
       '@codemirror/text': 0.19.5
       '@codemirror/view': 0.19.12
@@ -3912,8 +3914,8 @@ packages:
       ufo: 0.7.9
     dev: false
 
-  /@nuxt/kit-edge/3.0.0-27267816.6bd7186:
-    resolution: {integrity: sha512-OCinQR1TBeZmgU61of/YM55JvY+3emO6D4BwfTNYe8KwqIpynYcrZJxlxAYRYZKzqfZhtqLnD1B8DJ1f2b6thQ==}
+  /@nuxt/kit-edge/3.0.0-27268729.5b8e10f:
+    resolution: {integrity: sha512-m7bzSe8NRuR7ZcZqKwBQfOb1y3AlmGIiNMibZVO4r0ggIfT4fmkzVwfdZm+oWNhOYlZET7Kyk08rSjwShENukQ==}
     engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0}
     dependencies:
       consola: 2.15.3
@@ -3934,7 +3936,7 @@ packages:
       std-env: 3.0.0
       ufo: 0.7.9
       unctx: 1.0.2
-      untyped: 0.2.11
+      untyped: 0.2.12
     dev: true
 
   /@nuxt/loading-screen/2.0.4:
@@ -14036,7 +14038,7 @@ packages:
   /nuxt-windicss/2.0.11:
     resolution: {integrity: sha512-/vAEmKLq1Iomuj4lz751dsoXdlGVAoiEGSh3JVxuZJMkqc/yrHTQrNhtMaOQzx5heuVsQ+E2bIF+Q/tfxicOFQ==}
     dependencies:
-      '@nuxt/kit': /@nuxt/kit-edge/3.0.0-27267816.6bd7186
+      '@nuxt/kit': /@nuxt/kit-edge/3.0.0-27268729.5b8e10f
       defu: 5.0.0
       h3: 0.3.3
       listhen: 0.2.5
@@ -18167,8 +18169,8 @@ packages:
       has-value: 0.3.1
       isobject: 3.0.1
 
-  /untyped/0.2.11:
-    resolution: {integrity: sha512-KVNcu9jB+mlnQJiunAzmqpnnn9R+yniT+AkOk9ZgCIsThwh0nlP6wO+O7mJjHM7Y2yplEu3v6NNtRvb82+uGxw==}
+  /untyped/0.2.12:
+    resolution: {integrity: sha512-mdMpwUHnJUQDpEmuByMuLxYdrPVlA98a1/b8MgoFoasR5tJTfsNbhAPfceAgFqMd/E05427T7MXgJGqdtDF/bQ==}
     dev: true
 
   /upath/1.2.0: