feat: introduce graphql mode for codemirror

This commit is contained in:
Andrew Bastin
2021-09-10 19:00:18 +05:30
parent 41a02f059d
commit 003400cfa8
4 changed files with 85 additions and 1 deletions

View File

@@ -300,6 +300,7 @@ import { getCurrentStrategyID } from "~/helpers/network"
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest" import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
import { useCodemirror } from "~/helpers/editor/codemirror" import { useCodemirror } from "~/helpers/editor/codemirror"
import "codemirror/mode/javascript/javascript" import "codemirror/mode/javascript/javascript"
import "~/helpers/editor/modes/graphql"
import jsonLinter from "~/helpers/editor/linting/json" import jsonLinter from "~/helpers/editor/linting/json"
import { createGQLQueryLinter } from "~/helpers/editor/linting/gqlQuery" import { createGQLQueryLinter } from "~/helpers/editor/linting/gqlQuery"
import queryCompleter from "~/helpers/editor/completion/gqlQuery" import queryCompleter from "~/helpers/editor/completion/gqlQuery"
@@ -365,7 +366,7 @@ const schemaString = useReadonlyStream(props.conn.schema$, null)
useCodemirror(queryEditor, gqlQueryString, { useCodemirror(queryEditor, gqlQueryString, {
extendedEditorConfig: { extendedEditorConfig: {
mode: "application/ld+json", mode: "graphql",
}, },
linter: createGQLQueryLinter(schemaString), linter: createGQLQueryLinter(schemaString),
completer: queryCompleter(schemaString), completer: queryCompleter(schemaString),

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2021 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
import CodeMirror from "codemirror"
import {
LexRules,
ParseRules,
isIgnored,
onlineParser,
State,
} from "graphql-language-service-parser"
/**
* The GraphQL mode is defined as a tokenizer along with a list of rules, each
* of which is either a function or an array.
*
* * Function: Provided a token and the stream, returns an expected next step.
* * Array: A list of steps to take in order.
*
* A step is either another rule, or a terminal description of a token. If it
* is a rule, that rule is pushed onto the stack and the parsing continues from
* that point.
*
* If it is a terminal description, the token is checked against it using a
* `match` function. If the match is successful, the token is colored and the
* rule is stepped forward. If the match is unsuccessful, the remainder of the
* rule is skipped and the previous rule is advanced.
*
* This parsing algorithm allows for incremental online parsing within various
* levels of the syntax tree and results in a structured `state` linked-list
* which contains the relevant information to produce valuable typeaheads.
*/
CodeMirror.defineMode("graphql", (config) => {
const parser = onlineParser({
eatWhitespace: (stream) => stream.eatWhile(isIgnored),
lexRules: LexRules,
parseRules: ParseRules,
editorConfig: { tabSize: 2 },
})
return {
config,
startState: parser.startState,
token: parser.token as unknown as CodeMirror.Mode<any>["token"], // TODO: Check if the types are indeed compatible
indent,
electricInput: /^\s*[})\]]/,
fold: "brace",
lineComment: "#",
closeBrackets: {
pairs: '()[]{}""',
explode: "()[]{}",
},
}
})
// Seems the electricInput type in @types/codemirror is wrong (i.e it is written as electricinput instead of electricInput)
function indent(
this: CodeMirror.Mode<any> & {
electricInput?: RegExp
config?: CodeMirror.EditorConfiguration
},
state: State,
textAfter: string
) {
const levels = state.levels
// If there is no stack of levels, use the current level.
// Otherwise, use the top level, pre-emptively dedenting for close braces.
const level =
!levels || levels.length === 0
? state.indentLevel
: levels[levels.length - 1] -
(this.electricInput?.test(textAfter) ? 1 : 0)
return (level || 0) * (this.config?.indentUnit || 0)
}

2
package-lock.json generated
View File

@@ -5,6 +5,7 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "hoppscotch",
"version": "2.0.0", "version": "2.0.0",
"dependencies": { "dependencies": {
"@apollo/client": "^3.4.10", "@apollo/client": "^3.4.10",
@@ -25,6 +26,7 @@
"fuse.js": "^6.4.6", "fuse.js": "^6.4.6",
"graphql": "^15.5.0", "graphql": "^15.5.0",
"graphql-language-service-interface": "^2.8.4", "graphql-language-service-interface": "^2.8.4",
"graphql-language-service-parser": "^1.9.2",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mustache": "^4.2.0", "mustache": "^4.2.0",

View File

@@ -41,6 +41,7 @@
"fuse.js": "^6.4.6", "fuse.js": "^6.4.6",
"graphql": "^15.5.0", "graphql": "^15.5.0",
"graphql-language-service-interface": "^2.8.4", "graphql-language-service-interface": "^2.8.4",
"graphql-language-service-parser": "^1.9.2",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mustache": "^4.2.0", "mustache": "^4.2.0",