Skip to content

Commit e48bdce

Browse files
authored
Merge pull request #1513 from swagger-api/feature/jump-to-plugin
move jumpToPath into its own plugin
2 parents 4e7dfb1 + b04c060 commit e48bdce

File tree

6 files changed

+118
-9
lines changed

6 files changed

+118
-9
lines changed

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import EditorAutosuggestSnippetsPlugin from "./plugins/editor-autosuggest-snippe
1212
import EditorAutosuggestKeywordsPlugin from "./plugins/editor-autosuggest-keywords"
1313
import EditorAutosuggestOAS3KeywordsPlugin from "./plugins/editor-autosuggest-oas3-keywords"
1414
import EditorAutosuggestRefsPlugin from "./plugins/editor-autosuggest-refs"
15+
import JumpToPathPlugin from "./plugins/jump-to-path"
1516

1617
// eslint-disable-next-line no-undef
1718
const { GIT_DIRTY, GIT_COMMIT, PACKAGE_VERSION } = buildInfo
@@ -28,6 +29,7 @@ const plugins = {
2829
EditorAutosuggestKeywordsPlugin,
2930
EditorAutosuggestRefsPlugin,
3031
EditorAutosuggestOAS3KeywordsPlugin,
32+
JumpToPathPlugin,
3133
}
3234

3335
const defaults = {

src/plugins/editor/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import makeEditor from "./components/editor"
22
import EditorContainer from "./components/editor-container"
3-
import JumpToPath from "./components/jump-to-path"
43
import * as actions from "./actions"
54
import reducers from "./reducers"
65
import * as selectors from "./selectors"
@@ -11,7 +10,7 @@ let Editor = makeEditor({
1110

1211
export default function () {
1312
return {
14-
components: { Editor, EditorContainer, JumpToPath },
13+
components: { Editor, EditorContainer },
1514
statePlugins: {
1615
editor: {
1716
reducers,

src/plugins/editor/components/jump-to-path.jsx renamed to src/plugins/jump-to-path/components.jsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,43 @@
11
import React, { PropTypes } from "react"
22
import JumpIcon from "./jump-icon.svg"
33

4-
export default class JumpToPath extends React.Component {
4+
export class JumpToPath extends React.Component {
55
static propTypes = {
66
editorActions: PropTypes.object.isRequired,
77
specSelectors: PropTypes.object.isRequired,
88
fn: PropTypes.object.isRequired,
99
path: PropTypes.oneOfType([
1010
PropTypes.array,
1111
PropTypes.string
12-
]).isRequired,
12+
]),
1313
content: PropTypes.element,
14-
showButton: PropTypes.bool
14+
showButton: PropTypes.bool,
15+
specPath: PropTypes.array, // The location within the spec. Used as a fallback if `path` doesn't exist
16+
}
17+
18+
static defaultProps = {
19+
path: "",
1520
}
1621

1722
shouldComponentUpdate(nextProps) {
1823
let { shallowEqualKeys } = nextProps.fn
1924
return shallowEqualKeys(this.props, nextProps, [
20-
"content", "showButton", "path"
25+
"content", "showButton", "path", "specPath"
2126
])
2227
}
2328

2429
jumpToPath = (e) => {
2530
e.stopPropagation()
2631

27-
let { path, fn: { AST, transformPathToArray }, specSelectors: { specStr, specJson }, editorActions } = this.props
28-
let line = AST.getLineNumberForPath(specStr(), typeof path === "string" ? transformPathToArray(path, specJson().toJS()) : path)
29-
editorActions.jumpToLine(line)
32+
const {
33+
specPath=[],
34+
path,
35+
specSelectors,
36+
editorActions
37+
} = this.props
38+
39+
const jumpPath = specSelectors.bestJumpPath({path, specPath})
40+
editorActions.jumpToLine(specSelectors.getSpecLineFromPath(jumpPath))
3041
}
3142

3243

src/plugins/jump-to-path/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import spec from "./spec"
2+
import * as components from "./components"
3+
4+
export default function JumpToPathPlugin() {
5+
return [
6+
spec,
7+
{
8+
components,
9+
}
10+
]
11+
}

src/plugins/jump-to-path/spec.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
export default function spec() {
2+
return {
3+
statePlugins: {
4+
spec: {
5+
selectors: {
6+
7+
getSpecLineFromPath: (state, path) => ({fn: { AST }, specSelectors: { specStr }}) => {
8+
return AST.getLineNumberForPath(specStr(), path)
9+
},
10+
11+
// This will search return `path if it exists, else it'll look for the best $ref jump point
12+
// There is one caveat, it'll not search _down_ for deeply nested $refs. In those cases, it'll bring you to the shallower $ref.
13+
bestJumpPath: (state, {path, specPath}) => (system) => {
14+
const {
15+
specSelectors: { specJson },
16+
fn: { transformPathToArray }
17+
} = system
18+
19+
// We"ve been given an explicit path? Use that...
20+
if(path) {
21+
return typeof path === "string" ? transformPathToArray(path, specJson().toJS()) : path
22+
}
23+
24+
// Try each path in the resolved spec, starting from the deepest
25+
for(let i = specPath.length; i >= 0; i--) {
26+
const tryPath = specPath.slice(0,i)
27+
28+
// A $ref exists in the source? ( ie: pre-resolver)
29+
const $ref = specJson().getIn([...tryPath, "$ref"])
30+
// We have a $ref in the source?
31+
if($ref) {
32+
if(!/^#\//.test($ref)) {
33+
return [...tryPath, "$ref"]
34+
} else { // Is local $ref
35+
// Get rid of the trailing '#'
36+
const pointer = $ref.charAt(0) === "#" ? $ref.substr(1) : $ref
37+
return jsonPointerToArray(pointer)
38+
}
39+
}
40+
41+
// This path exists in the source spec?
42+
if(specJson().hasIn(tryPath)) {
43+
return tryPath
44+
}
45+
}
46+
47+
// ...else just specPath, which is hopefully close enough
48+
return specPath
49+
}
50+
}
51+
}
52+
}
53+
}
54+
}
55+
56+
// Copied out of swagger-client, not sure if it should be exposed as a lib or as part of the public swagger-client api.
57+
/**
58+
* Converts a JSON pointer to array.
59+
* @api public
60+
*/
61+
function jsonPointerToArray(pointer) {
62+
if (typeof pointer !== "string") {
63+
throw new TypeError(`Expected a string, got a ${typeof pointer}`)
64+
}
65+
66+
if (pointer[0] === "/") {
67+
pointer = pointer.substr(1)
68+
}
69+
70+
if (pointer === "") {
71+
return []
72+
}
73+
74+
return pointer.split("/").map(unescapeJsonPointerToken)
75+
}
76+
77+
/**
78+
* Unescapes a JSON pointer.
79+
* @api public
80+
*/
81+
function unescapeJsonPointerToken(token) {
82+
if (typeof token !== "string") {
83+
return token
84+
}
85+
return token.replace(/~1/g, "/").replace(/~0/g, "~")
86+
}

0 commit comments

Comments
 (0)