├── .gitignore ├── CHANGELOG.md ├── keymaps └── go-types.json ├── spec ├── go-types-view-spec.js └── go-types-spec.js ├── styles └── go-types.less ├── menus └── go-types.json ├── package.json ├── README.md ├── LICENSE.md └── lib ├── go-types.js ├── components ├── components.js └── index.js └── go-types-view.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.0 - First Release 2 | * Every feature added 3 | * Every bug fixed 4 | -------------------------------------------------------------------------------- /keymaps/go-types.json: -------------------------------------------------------------------------------- 1 | { 2 | "atom-workspace": { 3 | "ctrl-alt-o": "go-types:toggle" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /spec/go-types-view-spec.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import GoTypesView from '../lib/go-types-view'; 4 | 5 | describe('GoTypesView', () => { 6 | it('has one valid test', () => { 7 | expect('life').toBe('easy'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /styles/go-types.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .go-types { 8 | } 9 | -------------------------------------------------------------------------------- /menus/go-types.json: -------------------------------------------------------------------------------- 1 | { 2 | "context-menu": { 3 | "atom-text-editor": [ 4 | { 5 | "label": "Toggle go-types", 6 | "command": "go-types:toggle" 7 | } 8 | ] 9 | }, 10 | "menu": [ 11 | { 12 | "label": "Packages", 13 | "submenu": [ 14 | { 15 | "label": "go-types", 16 | "submenu": [ 17 | { 18 | "label": "Toggle", 19 | "command": "go-types:toggle" 20 | } 21 | ] 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-types", 3 | "main": "./lib/go-types", 4 | "version": "1.2.1", 5 | "description": "View all Go types for currently active file", 6 | "keywords": [ 7 | "go", 8 | "golang", 9 | "google", 10 | "go-type", 11 | "go-types", 12 | "list-types" 13 | ], 14 | "activationCommands": { 15 | "atom-workspace": "go-types:toggle" 16 | }, 17 | "repository": "https://github.com/natdm/go-types-atom", 18 | "license": "MIT", 19 | "engines": { 20 | "atom": ">=1.17.0 <2.0.0" 21 | }, 22 | "dependencies": { 23 | "prop-types": "^15.5.10", 24 | "react": "^15.5.4", 25 | "react-dom": "^15.5.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-types 2 | 3 | Go Types View adds a list of all types in the currently active file in a panel to the left of the screen. The panel is updated after each save, and has click-to-navigate functionality. 4 | 5 | - Click `By Line` (default) to sort entries by line 6 | - Click `By Method` to group types with their method, overriding line numbers. 7 | - Click a method, type, or declaration to navigate to that declaration. 8 | 9 | Toggle open/close with `option-ctrl-o` (unless there's something else with that, then open with `cmd + shift + p` and search `go types` to toggle) 10 | 11 | ![Go-Types Screenshot](https://media.giphy.com/media/3ohzdZOltxYApnifRK/giphy.gif) 12 | 13 | Being a newly published package, it's not bullet-proof yet. If you have any issues, I welcome Github issues and will fix it as soon as I can. Typically the following day. 14 | 15 | Also keep an eye out for updates. 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/go-types.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import View from './go-types-view'; 4 | import { CompositeDisposable, Disposable } from 'atom'; 5 | import { Loading, Error, NoGo } from './components'; 6 | 7 | export default { 8 | subscriptions: null, 9 | activate(state) { 10 | this.subscriptions = new CompositeDisposable( 11 | // Add an opener for our view. 12 | atom.workspace.addOpener(uri => { 13 | if (uri === 'atom://go-types') { 14 | return new View(); 15 | } 16 | }), 17 | // Register command that toggles this view 18 | atom.commands.add('atom-workspace', { 19 | 'go-types:toggle': () => this.toggle(), 20 | }), 21 | // Destroy any ActiveEditorInfoViews when the package is deactivated. 22 | new Disposable(() => { 23 | atom.workspace.getPaneItems().forEach(item => { 24 | if (item instanceof View) { 25 | item.destroy(); 26 | } 27 | }); 28 | }), 29 | ); 30 | }, 31 | 32 | deactivate() { 33 | this.subscriptions.dispose(); 34 | }, 35 | 36 | toggle() { 37 | atom.workspace.toggle('atom://go-types'); 38 | }, 39 | 40 | deserializeActiveEditorInfoView(serialized) { 41 | return new View(); 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/components/components.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | import React from 'react'; 3 | 4 | const containerStyle = { 5 | height: '100%', 6 | overflowY: 'auto', 7 | marginTop: '1.5em', 8 | borderLeftWidth: 2, 9 | borderLeftColor: '#568af2', 10 | borderLeftStyle: 'inset', 11 | }; 12 | 13 | const msgTxtStyle = { 14 | color: 'rgba(161,167,180,.3)', 15 | textAlign: 'center', 16 | }; 17 | 18 | const errStyleText = { 19 | color: '#e06c75', 20 | textAlign: 'center', 21 | }; 22 | 23 | const asText = { background: 'none', border: 'none', margin: 0 }; 24 | 25 | const tabContainerStyle = { 26 | minWidth: '15em', 27 | width: '100%', 28 | position: 'absolute', 29 | top: 0, 30 | height: '1.5m', 31 | }; 32 | 33 | const tabStyle = { 34 | ...asText, 35 | width: '50%', 36 | }; 37 | const selectedClr = 'rgba(255,255,255,.8)'; 38 | const unselectedClr = 'rgba(161, 167, 180, 0.298039)'; 39 | const tabLeftStyle = selected => ({ 40 | ...tabStyle, 41 | color: selected ? selectedClr : unselectedClr, 42 | }); 43 | 44 | const tabRightStyle = selected => ({ 45 | ...tabStyle, 46 | color: selected ? selectedClr : unselectedClr, 47 | }); 48 | 49 | const codeSnippetStyle = { 50 | color: 'rgba(161,167,180,1)', 51 | }; 52 | 53 | const container = ({ children }) =>
{children}
; 54 | 55 | export const NoGo = () => ( 56 | 57 |

Not a go file

58 |
59 | ); 60 | export const Error = ({ error }) => ( 61 | 62 |

{error}

63 |
64 | ); 65 | 66 | export const LoadingMessage = ({ message }) => ( 67 | 68 |

{message}

69 |
70 | ); 71 | 72 | export const Tabs = ({ handleOnClick, byLine }) => ( 73 |
74 | 75 | 76 |
77 | ); 78 | -------------------------------------------------------------------------------- /spec/go-types-spec.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import GoTypes from '../lib/go-types'; 4 | 5 | // Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 6 | // 7 | // To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 8 | // or `fdescribe`). Remove the `f` to unfocus the block. 9 | 10 | describe('GoTypes', () => { 11 | let workspaceElement, activationPromise; 12 | 13 | beforeEach(() => { 14 | workspaceElement = atom.views.getView(atom.workspace); 15 | activationPromise = atom.packages.activatePackage('go-types'); 16 | }); 17 | 18 | describe('when the go-types:toggle event is triggered', () => { 19 | it('hides and shows the modal panel', () => { 20 | // Before the activation event the view is not on the DOM, and no panel 21 | // has been created 22 | expect(workspaceElement.querySelector('.go-types')).not.toExist(); 23 | 24 | // This is an activation event, triggering it will cause the package to be 25 | // activated. 26 | atom.commands.dispatch(workspaceElement, 'go-types:toggle'); 27 | 28 | waitsForPromise(() => { 29 | return activationPromise; 30 | }); 31 | 32 | runs(() => { 33 | expect(workspaceElement.querySelector('.go-types')).toExist(); 34 | 35 | let goTypesElement = workspaceElement.querySelector('.go-types'); 36 | expect(goTypesElement).toExist(); 37 | 38 | let goTypesPanel = atom.workspace.panelForItem(goTypesElement); 39 | expect(goTypesPanel.isVisible()).toBe(true); 40 | atom.commands.dispatch(workspaceElement, 'go-types:toggle'); 41 | expect(goTypesPanel.isVisible()).toBe(false); 42 | }); 43 | }); 44 | 45 | it('hides and shows the view', () => { 46 | // This test shows you an integration test testing at the view level. 47 | 48 | // Attaching the workspaceElement to the DOM is required to allow the 49 | // `toBeVisible()` matchers to work. Anything testing visibility or focus 50 | // requires that the workspaceElement is on the DOM. Tests that attach the 51 | // workspaceElement to the DOM are generally slower than those off DOM. 52 | jasmine.attachToDOM(workspaceElement); 53 | 54 | expect(workspaceElement.querySelector('.go-types')).not.toExist(); 55 | 56 | // This is an activation event, triggering it causes the package to be 57 | // activated. 58 | atom.commands.dispatch(workspaceElement, 'go-types:toggle'); 59 | 60 | waitsForPromise(() => { 61 | return activationPromise; 62 | }); 63 | 64 | runs(() => { 65 | // Now we can test for view visibility 66 | let goTypesElement = workspaceElement.querySelector('.go-types'); 67 | expect(goTypesElement).toBeVisible(); 68 | atom.commands.dispatch(workspaceElement, 'go-types:toggle'); 69 | expect(goTypesElement).not.toBeVisible(); 70 | }); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /lib/go-types-view.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | import ReactDOM from 'react-dom'; 3 | import React from 'react'; 4 | import View from './components'; 5 | import { NoGo, Error, LoadingMessage } from './components/components'; 6 | const exec = require('child_process').exec; 7 | const fs = require('fs'); 8 | 9 | // the compatible version of go-typelist 10 | const compatibleVersion = '0.0.3'; 11 | 12 | export default class GoTypesView { 13 | constructor(serializedState) { 14 | // Create root element 15 | this.element = document.createElement('div'); 16 | this.element.classList.add('go-types'); 17 | 18 | // check for updates to binary. 19 | if (this.execExists()) { 20 | exec(`go-typelist -v`, (err, stdout, stderr) => { 21 | if (err !== null) this.render(); 22 | 23 | if (err !== null || stdout.trim() !== compatibleVersion.trim()) { 24 | this.getExec((err, res) => { 25 | if (err !== null) { 26 | const errMsg = 27 | 'Error getting go-typelist Please run `go get -u github.com/natdm/go-typelist/.../` to get and install the most recent version of the go-typelist parser.'; 28 | this.render(, null, null, 'error updating go-typelist: ' + err); 29 | return; 30 | } 31 | this.render(, 'Updated go-typelist'); 32 | }); 33 | } 34 | this.start(); 35 | return; 36 | }); 37 | } 38 | this.render(); 39 | this.getExec((err, res) => { 40 | if (err !== null) { 41 | this.render(, null, null, 'error getting go-typelist: ' + err); 42 | return; 43 | } 44 | this.render(, 'Updated go-typelist'); 45 | this.start(); 46 | }); 47 | } 48 | 49 | // render can render a component and optionally log messages 50 | render = (component, info, warn, error) => { 51 | if (info) console.log(info); 52 | if (warn) console.warn(warn); 53 | if (error) console.error(error); 54 | ReactDOM.render(component, this.element); 55 | }; 56 | 57 | start = () => { 58 | this.setListeners(); 59 | this.parseFile(); 60 | }; 61 | 62 | execExists = () => { 63 | let stat; 64 | try { 65 | stat = fs.statSync(process.env.GOPATH + '/bin/go-typelist'); 66 | return true; 67 | } catch (e) { 68 | return false; 69 | } 70 | }; 71 | 72 | // attempt to get/update and return an error if any. Else, return stdout 73 | getExec = cb => exec(`go get -u github.com/natdm/go-typelist/.../`, (err, stdout, stderr) => (err !== null ? cb(err, null) : cb(null, stdout))); 74 | 75 | setListeners = () => { 76 | // on each save, parse the file. 77 | atom.workspace.observeTextEditors(editor => editor.onDidSave(() => this.parseFile())); 78 | 79 | // on each pane change, parse the file. 80 | atom.workspace.onDidChangeActivePaneItem(pane => (typeof atom.workspace.getActiveTextEditor != undefined ? this.parseFile() : null)); 81 | }; 82 | 83 | parseFile = () => { 84 | let editor, file; 85 | let entries = null; 86 | if ((editor = atom.workspace.getActivePaneItem())) { 87 | if (editor.buffer === null || typeof editor.buffer === 'undefined') { 88 | // when they click within GoTypes column 89 | return; 90 | } 91 | 92 | file = editor.buffer.file; 93 | if (file === null || typeof file === 'undefined') { 94 | this.render(); 95 | return; 96 | } 97 | 98 | if (!file.path.endsWith('.go')) { 99 | this.render(); 100 | return; 101 | } 102 | 103 | exec(`go-typelist ${file.path}`, (err, stdout, stderr) => { 104 | // stderr does not output null. Lame. 105 | if (stderr !== '') { 106 | if (stderr.indexOf('command not found') > -1) { 107 | this.render(); 108 | return; 109 | } 110 | } 111 | if (err !== null) { 112 | this.render(, null, null, 'could not open file' + err); 113 | return; 114 | } 115 | try { 116 | entries = JSON.parse(stdout); 117 | } catch (err) { 118 | this.render(, null, null, err); 119 | return; 120 | } 121 | 122 | this.render(); 123 | }); 124 | } 125 | }; 126 | 127 | // Used by Atom for tab text 128 | getTitle = () => 'Go Types'; 129 | 130 | // This location will be used if the user hasn't overridden it by dragging the item elsewhere. 131 | // Valid values are "left", "right", "bottom", and "center" (the default). 132 | getDefaultLocation = () => 'right'; 133 | 134 | // The locations into which the item can be moved. 135 | getAllowedLocations = () => ['left', 'right', 'bottom']; 136 | 137 | // Used by Atom to identify the view when toggling. 138 | getURI = () => 'atom://go-types'; 139 | 140 | // Returns an object that can be retrieved when package is activated 141 | serialize = () => ({ deserializer: 'go-types/GoTypes' }); 142 | 143 | // Tear down any state and detach 144 | destroy = () => this.element.remove(); 145 | 146 | getElement = () => this.element; 147 | } 148 | -------------------------------------------------------------------------------- /lib/components/index.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | import React, { Component } from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import PropTypes from 'prop-types'; 5 | import { Tabs } from './components'; 6 | 7 | const groupMethodsWithTypes = entries => { 8 | // group methods by type name 9 | let methods = {}; 10 | 11 | // group methods with types while still preserving line numbers for all non-methods 12 | // and remove methods from the original. 13 | return ( 14 | entries 15 | // Remove methods from entries and add to methods[receiver.type_name] 16 | .reduce((prev, curr, idx, arr) => { 17 | // if not a method, add it. 18 | if (curr.receiver === null) { 19 | return [...prev, { ...curr, has_parent: false }]; 20 | } 21 | 22 | // if is a method, but has no parent.. add it and return 23 | if (typeof arr.find(typ => typ.name === curr.receiver.type_name) === 'undefined') { 24 | return [...prev, { ...curr, has_parent: false }]; 25 | } 26 | 27 | // if method has parent in file, add to methods object above and do not return that method 28 | const currWithP = { ...curr, has_parent: true }; 29 | methods.hasOwnProperty(curr.receiver.type_name) 30 | ? (methods[curr.receiver.type_name] = [...methods[curr.receiver.type_name], currWithP]) 31 | : (methods[curr.receiver.type_name] = [currWithP]); 32 | 33 | return prev; 34 | }, []) 35 | // sort parent types to preserve lines 36 | .sort((a, b) => a.line - b.line) 37 | // add methods back in 38 | .reduce( 39 | (prev, curr) => (methods.hasOwnProperty(curr.name) ? [...prev, curr, ...methods[curr.name].sort((a, b) => a.line - b.line)] : [...prev, curr]), 40 | [], 41 | ) 42 | ); 43 | }; 44 | 45 | export default class View extends Component { 46 | constructor(p) { 47 | super(p); 48 | this.state = { byLine: true }; 49 | } 50 | 51 | navToItem(line) { 52 | atom.workspace.getCenter().observeActivePaneItem(item => { 53 | if (typeof item === 'undefined' || item === null || typeof item.setCursorBufferPosition !== 'function') { 54 | return; 55 | } 56 | try { 57 | // for some reason setCursorBufferPosition gets fired when the screen is not a code screen. 58 | item.setCursorBufferPosition([line - 1, 0]); 59 | } catch (e) { 60 | // do nothing 61 | } 62 | }); 63 | } 64 | 65 | stylize(entry) { 66 | const { signature, name, type, receiver, has_parent } = entry; 67 | 68 | const _syntaxSrc = 'syntax--source syntax--go', 69 | _keyWord = 'syntax--keyword syntax--type syntax--go', 70 | _fn = 'syntax--entity syntax--name syntax--function syntax--go', 71 | _typeName = 'syntax--entity syntax--name syntax--type syntax--go', 72 | _struct = 'syntax--keyword syntax--struct syntax--go', 73 | _string = 'syntax--storage syntax--type syntax--string syntax--go', 74 | _num = 'syntax--storage syntax--type syntax--numeric syntax--go', 75 | _interface = 'syntax--keyword syntax--interface syntax--go', 76 | _variable = 'syntax--variable syntax--other syntax--assignment syntax--go'; 77 | 78 | let out = signature; 79 | 80 | out = out 81 | // .replace(/func/, `func `) 82 | // .replace(/struct/, `struct`) 83 | // .replace(/int/, `int`) 84 | 85 | // .replace(/type/, `type`) 86 | // .replace(/const/, `const`) 87 | // .replace(/var/, `var`) 88 | 89 | .replace(/interface/, `interface`) 90 | .replace(/string/, `string`) 91 | .replace(/int32/, `int32`) 92 | .replace(/int64/, `int64`) 93 | .replace(/uint/, `uint`) 94 | .replace(/uint32/, `uint32`) 95 | .replace(/uint64/, `uint64`) 96 | .replace(/float32/, `float32`) 97 | .replace(/float64/, `float64`) 98 | .replace(/map/, `map`) 99 | .replace(/array/, `array`) 100 | .replace(/slice/, `slice`) 101 | .replace(/byte/, `byte`) 102 | .replace(/bool/, `bool`) 103 | .replace(/uintptr/, `uintptr`) 104 | .replace(/chan/, `chan`) 105 | .replace(/complex64/, `complex64`) 106 | .replace(/error/, `error`) 107 | .replace(/complex128/, `complex128`); 108 | 109 | // if a method and the parent type is in this file, group by method 110 | if (receiver != null && has_parent && !this.state.byLine) { 111 | // if a pointer receiver, do something fancier. 112 | receiver.pointer 113 | ? (out = out.replace(`func (${receiver.alias} *${receiver.type_name})`, '  ┣')) 114 | : (out = out.replace(`func (${receiver.alias} ${receiver.type_name})`, '  ┝')); 115 | } 116 | 117 | if (out.split(' ')[0] == 'func') { 118 | out = out.replace('func ', `func `); 119 | } 120 | if (out.indexOf('struct') > -1 && out.indexOf('type') > -1) { 121 | out = out.replace('struct', `struct`); 122 | } 123 | if (out.indexOf(']int') > -1 || out.indexOf('(int') > -1 || out.indexOf('int)') > -1) { 124 | out = out.replace('int', `int`); 125 | } 126 | if (out.indexOf('struct {') > -1) { 127 | out = out.replace('struct {', `struct {`); 128 | } 129 | 130 | if (name != null) { 131 | switch (type) { 132 | case 'GenDecl': 133 | const sigSplit = signature.split(' '); 134 | out = `${sigSplit[0]} ${sigSplit[1]} ${sigSplit.reduce((p, c, i) => (i > 1 ? p + ' ' + c : p), '')}`; 135 | break; 136 | default: 137 | out = out.replace(name, `${name}`); 138 | } 139 | } 140 | 141 | return out; 142 | } 143 | 144 | sort = entries => (this.state.byLine ? entries.sort((a, b) => a.line - b.line) : groupMethodsWithTypes(entries)); 145 | 146 | toggleSort = () => { 147 | this.setState({ byLine: !this.state.byLine }); 148 | }; 149 | 150 | handleTabclick = byLine => { 151 | if (this.state.byLine !== byLine) { 152 | this.toggleSort(); 153 | } 154 | }; 155 | 156 | render() { 157 | const containerStyle = { height: '100%', overflowY: 'auto', marginTop: '1.5em' }, 158 | htmlSpanStyle = { lineHeight: 1.7, fontSize: '1.1em' }, 159 | lineNumStyle = { 160 | display: 'block', 161 | float: 'left', 162 | width: '2.5em', 163 | position: 'relative', 164 | }, 165 | buttonStyle = { ...asText, color: 'orange', marginLeft: '.5em' }, 166 | ulStyle = { zIndex: 0, listStyleType: 'none', paddingLeft: '0', marginTop: '.5em' }, 167 | liStyle = { 168 | paddingLeft: '.5em', 169 | lineHeight: '1.9em', 170 | overflow: 'hidden', 171 | whiteSpace: 'nowrap', 172 | alignItems: 'stretch', 173 | position: 'relative', 174 | }, 175 | asText = { background: 'none', border: 'none', margin: 0 }, 176 | lineItemBtn = { position: 'absolute', width: '100%', textAlign: 'left' }; 177 | 178 | const { entries } = this.props; 179 | 180 | return ( 181 |
182 | this.handleTabclick(byLine)} byLine={this.state.byLine} /> 183 |
184 |
    185 | {this.sort(entries).map((e, i) => ( 186 |
  • {}} /* eventually make this change the background color */> 187 | {`${e.line}:`} 188 | 191 |
  • 192 | ))} 193 |
194 |
195 |
196 | ); 197 | } 198 | } 199 | 200 | View.propTypes = { 201 | entries: PropTypes.arrayOf( 202 | PropTypes.shape({ 203 | line: PropTypes.number, 204 | signature: PropTypes.string, 205 | type: PropTypes.number, 206 | name: PropTypes.number, 207 | receiver: PropTypes.shape({ 208 | type_name: PropTypes.string, 209 | pointer: PropTypes.bool, 210 | alias: PropTypes.string, 211 | }), 212 | }), 213 | ), 214 | }; 215 | --------------------------------------------------------------------------------