Skip to content

Commit e3580b0

Browse files
authored
Merge pull request #1367 from swagger-api/feature/autocomplete-plugins
Refactor autosuggest(complete) into plugins.
2 parents 7d7353c + 42fd7c7 commit e3580b0

29 files changed

+322
-189
lines changed

dist/swagger-editor-standalone-preset.js

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/swagger-editor.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/swagger-editor.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/swagger-editor.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/validation.worker.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import "swagger-ui/dist/swagger-ui.css"
66
import EditorPlugin from "./plugins/editor"
77
import LocalStoragePlugin from "./plugins/local-storage"
88
import ValidationApiPlugin from "./plugins/validation/apis"
9+
import EditorAutosuggestPlugin from "./plugins/editor-autosuggest"
10+
import EditorAutosuggestSnippetsPlugin from "./plugins/editor-autosuggest-snippets"
11+
import EditorAutosuggestKeywordsPlugin from "./plugins/editor-autosuggest-keywords"
12+
import EditorAutosuggestRefsPlugin from "./plugins/editor-autosuggest-refs"
913

1014
// eslint-disable-next-line no-undef
1115
const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo
@@ -15,7 +19,11 @@ window.versions.swaggerEditor = `${PACKAGE_VERSION}/${GIT_COMMIT || "unknown"}${
1519
const plugins = {
1620
EditorPlugin,
1721
ValidationApiPlugin,
18-
LocalStoragePlugin
22+
LocalStoragePlugin,
23+
EditorAutosuggestPlugin,
24+
EditorAutosuggestSnippetsPlugin,
25+
EditorAutosuggestKeywordsPlugin,
26+
EditorAutosuggestRefsPlugin,
1927
}
2028

2129
const defaults = {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import keywordMap from "./keyword-map"
2+
import getKeywordsForPath from "./get-keywords-for-path"
3+
4+
export default function getCompletions(editor, session, pos, prefix, cb, ctx, system) {
5+
6+
const { fn: { getPathForPosition } } = system
7+
const { AST } = ctx
8+
var editorValue = editor.getValue()
9+
const path = getPathForPosition({ pos, prefix, editorValue, AST})
10+
11+
const suggestions = getKeywordsForPath({ system, path, keywordMap })
12+
cb(null, suggestions)
13+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import isArray from "lodash/isArray"
22
import isObject from "lodash/isObject"
3-
import get from "lodash/get"
4-
import last from "lodash/last"
53
import mapValues from "lodash/mapValues"
4+
import isPlainObject from "lodash/isPlainObject"
65
import toArray from "lodash/toArray"
76
import isString from "lodash/isString"
8-
import YAML from "js-yaml"
97

10-
//eslint-disable-next-line no-unused-vars
11-
export function getKeywordsForPath({ path, prefix, currentLine, editorValue, keywordMap }) {
8+
export default function getKeywordsForPath({ system, path, keywordMap}) {
129
keywordMap = Object.assign({}, keywordMap)
1310

1411
// is getting path was not successful stop here and return no candidates
@@ -23,21 +20,34 @@ export function getKeywordsForPath({ path, prefix, currentLine, editorValue, key
2320
]
2421
}
2522

26-
if(last(path) === "$ref") {
27-
let context = getContextType(path)
28-
let $refsForContext = $refablesForPath(editorValue, [context])
29-
return $refsForContext
30-
}
31-
3223
if(path[path.length - 2] === "tags" && path.length > 2) {
3324
// 'path.length > 2' excludes top-level 'tags'
34-
return getGlobalCompletions(editorValue, ["tags"], "name")
25+
return system.specSelectors.tags().map(tag => ({
26+
score: 0,
27+
meta: "local",
28+
value: tag.get("name"),
29+
})).toJS()
3530
}
3631

3732
let reversePath = path.slice(0).reverse()
3833
if(reversePath[1] === "security" && isNumeric(reversePath[0])) {
3934
// **.security[x]
40-
return getGlobalCompletions(editorValue, ["securityDefinitions"], "name")
35+
return system.specSelectors.securityDefinitions().keySeq().map(sec => ({
36+
score: 0,
37+
meta: "local",
38+
caption: sec,
39+
snippet: `${sec}: []`
40+
})).toJS()
41+
}
42+
43+
if(reversePath[0] === "security") {
44+
// **.security:
45+
return system.specSelectors.securityDefinitions().keySeq().map(sec => ({
46+
score: 0,
47+
meta: "local",
48+
caption: sec,
49+
snippet: `\n- ${sec}: []`
50+
})).toJS()
4151
}
4252

4353
// traverse down the keywordMap for each key in the path until there is
@@ -65,20 +75,18 @@ export function getKeywordsForPath({ path, prefix, currentLine, editorValue, key
6575
// suggest for array items
6676
if (isArray(keywordMap)) {
6777
if(isArray(keywordMap[0])) {
68-
let indent = ""
6978
return keywordMap[0].map(item => {
7079
return {
7180
name: "array",
72-
value: indent + "- " + item,
81+
value: "- " + item,
7382
score: 300,
7483
meta: "array item"
7584
}
7685
})
7786
} else {
78-
let indent = ""
7987
return [{
8088
name: "array",
81-
value: indent + "- ",
89+
value: "- ",
8290
score: 300,
8391
meta: "array item"
8492
}]
@@ -92,8 +100,7 @@ export function getKeywordsForPath({ path, prefix, currentLine, editorValue, key
92100

93101
// for each key in keywordMap map construct a completion candidate and
94102
// return the array
95-
return formatKeywordMap(keywordMap)
96-
.map(constructAceCompletion.bind(null, "keyword"))
103+
return suggestionFromSchema(keywordMap)
97104
}
98105

99106
function getChild(object, key) {
@@ -119,96 +126,45 @@ function getChild(object, key) {
119126
}
120127
}
121128

122-
function formatKeywordMap(map) {
123-
let res = mapValues(map, (val, key) => {
124-
return val.__value === undefined ? key : val.__value
125-
})
129+
function suggestionFromSchema(map) {
130+
const res = toArray(mapValues(map, (val, key) => {
131+
const keyword = val.__value === undefined ? key : val.__value
132+
const meta = isPlainObject(val) ? "object" : "keyword"
126133

127-
return toArray(res)
134+
return constructAceCompletion(meta, keyword)
135+
}))
136+
return res
128137
}
129138

130139
function constructAceCompletion(meta, keyword) {
131140
if(keyword.slice(0, 2) === "__") {
132141
return {}
133142
}
134143

135-
return {
136-
name: keyword,
137-
value: keyword,
138-
score: 300,
139-
meta: meta || "keyword"
144+
// Give keywords, that extra colon
145+
let snippet
146+
switch(meta) {
147+
case "keyword":
148+
snippet = `${keyword}: `
149+
break
150+
case "object":
151+
snippet = `${keyword}:\n `
152+
break
153+
default:
154+
snippet = keyword
140155
}
141-
}
142156

143-
function $refablesForPath(specStr, path) {
144-
let refs = Object.keys(getJSFromYaml(specStr, path) || {})
157+
// snippet's treat `$` as special characters
158+
snippet = snippet.replace("$", "\\$")
145159

146-
let completions = refs.map(ref => {
147-
return {
148-
score: 0,
149-
meta: "local",
150-
value: `'#/${path.join("/")}/${ref}'`,
151-
caption: ref
152-
}
153-
})
154-
155-
return completions || []
156-
}
157-
158-
function getGlobalCompletions(specStr, path, prop) {
159-
let items = getJSFromYaml(specStr, path) || []
160-
if(isArray(items)) {
161-
return items
162-
.filter(item => !!item)
163-
.map(item => prop ? item[prop] : item)
164-
.map(name => {
165-
if(!name) { return {} }
166-
return {
167-
score: 0,
168-
meta: "local",
169-
value: name
170-
}
171-
})
172-
173-
} else {
174-
return Object.keys(mapValues(items, (item) => {
175-
return prop ? item[prop] : item
176-
})).map(name => {
177-
if(!name) { return {} }
178-
return {
179-
score: 0,
180-
meta: "local",
181-
value: name
182-
}
183-
})
160+
return {
161+
snippet,
162+
caption: keyword,
163+
score: 300,
164+
meta,
184165
}
185166
}
186167

187-
function getJSFromYaml(yaml, path = []) {
188-
let obj = YAML.safeLoad(yaml)
189-
let sub = get(obj, path)
190-
return sub
191-
}
192-
193168
function isNumeric(obj) {
194169
return !isNaN(obj)
195170
}
196-
197-
export function getContextType(path) {
198-
let contextTypes = {
199-
"paths": "pathitems",
200-
"definitions": "definitions",
201-
"schema": "definitions",
202-
"parameters": "parameters",
203-
"responses": "responses"
204-
}
205-
206-
for( var i=path.length-1; i>-1; i-- ) {
207-
let tag = path[i]
208-
209-
if( contextTypes[tag] ) {
210-
return contextTypes[tag]
211-
}
212-
}
213-
return null
214-
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as wrapActions from "./wrap-actions"
2+
3+
export default function EditorAutosuggestKeywordsPlugin() {
4+
return {
5+
statePlugins: {
6+
editor: {
7+
wrapActions,
8+
}
9+
}
10+
}
11+
}

0 commit comments

Comments
 (0)