Added Swagger
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
// Enable Ace editor autocompletions
|
||||
export const enableAutocompletions = ({editor}) => () => {
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true
|
||||
})
|
||||
}
|
||||
|
||||
// Add completers. Just override this method. And concat on your completer(s)
|
||||
// see: https://github.com/ajaxorg/ace/blob/master/lib/ace/autocomplete.js
|
||||
// eg: return ori(...args).concat({ getCompletions() {...}})
|
||||
export const addAutosuggestionCompleters = () => () => {
|
||||
return []
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
export function getPathForPosition({ pos: originalPos, prefix, editorValue, AST }) {
|
||||
var pos = Object.assign({}, originalPos)
|
||||
var lines = editorValue.split(/\r\n|\r|\n/)
|
||||
var previousLine = lines[pos.row - 1] || ""
|
||||
var currentLine = lines[pos.row]
|
||||
var nextLine = lines[pos.row + 1] || ""
|
||||
var prepared = false
|
||||
|
||||
// we're always at the document root when there's no indentation,
|
||||
// so let's save some effort
|
||||
if (pos.column === 1) {
|
||||
return []
|
||||
}
|
||||
|
||||
let prevLineIndent = getIndent(previousLine).length
|
||||
let currLineIndent = getIndent(currentLine).length
|
||||
|
||||
const isCurrentLineEmpty = currentLine.replace(prefix, "").trim() === ""
|
||||
|
||||
if(
|
||||
(previousLine.trim()[0] === "-" || nextLine.trim()[0] === "-")
|
||||
&& currLineIndent >= prevLineIndent
|
||||
&& isCurrentLineEmpty
|
||||
) {
|
||||
// for arrays with existing items under it, on blank lines
|
||||
// example:
|
||||
// myArray:
|
||||
// - a: 1
|
||||
// | <-- user cursor
|
||||
currentLine += "- a: b" // fake array item
|
||||
// pos.column += 1
|
||||
prepared = true
|
||||
}
|
||||
|
||||
// if current position is in at a free line with whitespace insert a fake
|
||||
// key value pair so the generated AST in ASTManager has current position in
|
||||
// editing node
|
||||
if ( !prepared && isCurrentLineEmpty) {
|
||||
currentLine += "a: b" // fake key value pair
|
||||
pos.column += 1
|
||||
prepared = true
|
||||
}
|
||||
|
||||
if(currentLine[currentLine.length - 1] === ":") {
|
||||
// Add a space if a user doesn't put one after a colon
|
||||
// NOTE: this doesn't respect the "prepared" flag.
|
||||
currentLine += " "
|
||||
pos.column += 1
|
||||
}
|
||||
|
||||
//if prefix is empty then add fake, empty value
|
||||
if( !prepared && !prefix){
|
||||
// for scalar values with no values
|
||||
// i.e. "asdf: "
|
||||
currentLine += "~"
|
||||
}
|
||||
|
||||
// append inserted character in currentLine for better AST results
|
||||
lines[originalPos.row] = currentLine
|
||||
editorValue = lines.join("\n")
|
||||
|
||||
let path = AST.pathForPosition(editorValue, {
|
||||
line: pos.row,
|
||||
column: pos.column
|
||||
})
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
function getIndent(str) {
|
||||
let match = str.match(/^ +/)
|
||||
return match ? match[0] : ""
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
export function wrapCompleters(completers, cutoff = 100) {
|
||||
let isLiveCompletionDisabled = false
|
||||
let lastSpeeds = []
|
||||
let isPerformant = () => lastSpeeds.every(speed => speed < cutoff)
|
||||
|
||||
if(cutoff === 0 || cutoff === "0") {
|
||||
// never disable live autocomplete
|
||||
return completers
|
||||
}
|
||||
|
||||
return completers.map((completer, i) => {
|
||||
let ori = completer.getCompletions
|
||||
completer.getCompletions = function(editor, session, pos, prefix, callback) {
|
||||
let startTime = Date.now()
|
||||
try {
|
||||
ori(editor, session, pos, prefix, (...args) => {
|
||||
let msElapsed = Date.now() - startTime
|
||||
lastSpeeds[i] = msElapsed
|
||||
|
||||
if(isLiveCompletionDisabled && isPerformant()) {
|
||||
console.warn("Manual autocomplete was performant - re-enabling live autocomplete")
|
||||
editor.setOptions({
|
||||
enableLiveAutocompletion: true
|
||||
})
|
||||
isLiveCompletionDisabled = false
|
||||
}
|
||||
|
||||
if(msElapsed > cutoff && editor.getOption("enableLiveAutocompletion")) {
|
||||
console.warn("Live autocomplete is slow - disabling it")
|
||||
editor.setOptions({
|
||||
enableLiveAutocompletion: false
|
||||
})
|
||||
isLiveCompletionDisabled = true
|
||||
}
|
||||
|
||||
callback(...args)
|
||||
})
|
||||
} catch(e) {
|
||||
console.error("Autocompleter encountered an error")
|
||||
console.error(e)
|
||||
callback(null, [])
|
||||
}
|
||||
}
|
||||
return completer
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import * as actions from "./actions"
|
||||
import * as fn from "./fn"
|
||||
import * as specSelectors from "./spec-selectors"
|
||||
import { wrapCompleters } from "./helpers"
|
||||
|
||||
export default function EditorAutosuggestPlugin() {
|
||||
return {
|
||||
fn,
|
||||
statePlugins: {
|
||||
spec: {
|
||||
selectors: specSelectors,
|
||||
},
|
||||
editor: {
|
||||
actions,
|
||||
wrapActions: {
|
||||
onLoad: (ori, sys) => (context) => {
|
||||
const { editor } = context
|
||||
|
||||
// Any other calls for editor#onLoad
|
||||
ori(context)
|
||||
|
||||
// Enable autosuggestions ( aka: autocompletions )
|
||||
sys.editorActions.enableAutocompletions(context)
|
||||
|
||||
// Add completers ( for autosuggestions )
|
||||
const completers = sys.editorActions.addAutosuggestionCompleters(context)
|
||||
const cutoff = sys.getConfigs().liveAutocompleteCutoff
|
||||
const wrappedCompleters = wrapCompleters(completers || [], cutoff)
|
||||
editor.completers = wrappedCompleters
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { Set, Map } from "immutable"
|
||||
import { escapeJsonPointerToken } from "../refs-util"
|
||||
|
||||
const SWAGGER2_REF_MAP = {
|
||||
"paths": "pathitems",
|
||||
"definitions": "definitions",
|
||||
"schema": "definitions",
|
||||
"parameters": "parameters",
|
||||
"responses": "responses"
|
||||
}
|
||||
|
||||
const OAS3_REF_MAP = {
|
||||
schemas: "components/schemas", // for Schemas within Components
|
||||
schema: "components/schemas", // for Schemas throughout document
|
||||
parameters: "components/parameters",
|
||||
requestBody: "components/requestBodies",
|
||||
callbacks: "components/callbacks",
|
||||
examples: "components/examples",
|
||||
responses: "components/responses",
|
||||
headers: "components/headers",
|
||||
links: "components/links"
|
||||
}
|
||||
|
||||
const SWAGGER2_TYPES = Set(Object.values(SWAGGER2_REF_MAP))
|
||||
const OAS3_TYPES = Set(Object.values(OAS3_REF_MAP))
|
||||
|
||||
// Return a normalized "type" for a given path [a,b,c]
|
||||
// eg: /definitions/bob => definition
|
||||
// /paths/~1pets/responses/200/schema => definition ( because of schema )
|
||||
export const getRefType = (state, path) => (sys) => createSelector(
|
||||
() => {
|
||||
for( var i=path.length-1; i>-1; i-- ) {
|
||||
let tag = path[i]
|
||||
if(sys.specSelectors.isOAS3 && sys.specSelectors.isOAS3()) {
|
||||
if(OAS3_REF_MAP[tag]) {
|
||||
return OAS3_REF_MAP[tag]
|
||||
}
|
||||
} else if( SWAGGER2_REF_MAP[tag] ) {
|
||||
return SWAGGER2_REF_MAP[tag]
|
||||
}
|
||||
}
|
||||
return null
|
||||
})(state)
|
||||
|
||||
export const localRefs = (state) => (sys) => createSelector(
|
||||
sys.specSelectors.spec,
|
||||
sys.specSelectors.isOAS3 || (() => false),
|
||||
(spec, isOAS3) => {
|
||||
return (isOAS3 ? OAS3_TYPES : SWAGGER2_TYPES).toList().flatMap( type => {
|
||||
return spec
|
||||
.getIn(type.split("/"), Map({}))
|
||||
.keySeq()
|
||||
.map( name => Map({
|
||||
name,
|
||||
type,
|
||||
$ref: `#/${type}/${escapeJsonPointerToken(name)}`,
|
||||
}))
|
||||
})
|
||||
}
|
||||
)(state)
|
||||
Reference in New Issue
Block a user