├── .gitignore ├── example.gif ├── CHANGELOG.md ├── spec ├── cljs-doc-view-spec.js └── cljs-doc-spec.js ├── styles └── cljs-doc.less ├── lib ├── cljs-doc.js ├── vars.js └── cljs-doc-view.js ├── README.md ├── package.json └── LICENSE.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman01la/atom-cljs-doc/master/example.gif -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.1 2 | 3 | - Use activation hook to reduce atom loading time e7ff90e 4 | 5 | # 0.1.0 6 | 7 | - Initial release 8 | -------------------------------------------------------------------------------- /spec/cljs-doc-view-spec.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import CljsDocView from '../lib/cljs-doc-view'; 4 | 5 | describe('CljsDocView', () => { 6 | it('has one valid test', () => { 7 | expect('life').toBe('easy'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /styles/cljs-doc.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 | .cljs-doc { 8 | } 9 | -------------------------------------------------------------------------------- /lib/cljs-doc.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import CljsDocView from './cljs-doc-view'; 4 | 5 | export default { 6 | 7 | activate(state) { 8 | 9 | this.docView = new CljsDocView(); 10 | 11 | this.docView.initialize(); 12 | }, 13 | 14 | deactivate() { 15 | 16 | if (this.docView) { 17 | this.docView.destroy(); 18 | } 19 | this.docView = null; 20 | 21 | this.statusBar = null; 22 | }, 23 | 24 | consumeStatusBar(statusBar) { 25 | 26 | this.statusBar = statusBar; 27 | 28 | this.statusBar.addRightTile({ 29 | item: this.docView, 30 | priority: 10, 31 | }); 32 | } 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # atom-cljs-doc package 2 | 3 | *ClojureScript core library documentation viewer for Atom* 4 | 5 | ![atom-cljs-doc usage example gif](https://raw.githubusercontent.com/roman01la/atom-cljs-doc/master/example.gif) 6 | 7 | ## Installation 8 | 9 | Search package installer for `atom-cljs-doc` or use the command line: 10 | 11 | ``` 12 | apm install atom-cljs-doc 13 | ``` 14 | 15 | ## Usage 16 | 17 | 1. Open `.clj`, `.cljs` or `.cljc` file 18 | 2. Place text cursor into a form, e.g. `if` 19 | 3. Notice a button to the right in a status bar, e.g. `(doc if)` 20 | 4. Press it 21 | 5. A notification with a doc string will pop up 22 | 6. Read it, close or press `Read on ClojureDocs` button to go to documentation website 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "atom-cljs-doc", 3 | "main": "./lib/cljs-doc", 4 | "version": "0.1.1", 5 | "description": "ClojureScript core library documentation viewer for Atom", 6 | "keywords": [ 7 | "atom", 8 | "clojurescript", 9 | "clojure", 10 | "doc", 11 | "documentation" 12 | ], 13 | "activationHooks": [ 14 | "language-clojure:grammar-used" 15 | ], 16 | "repository": "https://github.com/roman01la/atom-cljs-doc", 17 | "license": "MIT", 18 | "engines": { 19 | "atom": ">=1.0.0 <2.0.0" 20 | }, 21 | "dependencies": { 22 | "clojurescript": "0.0.11" 23 | }, 24 | "consumedServices": { 25 | "status-bar": { 26 | "versions": { 27 | "^1.0.0": "consumeStatusBar" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Roman Liutikov 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/vars.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default [ 4 | "partial", 5 | "map", 6 | "zipmap", 7 | "rseq", 8 | "seq", 9 | "range", 10 | "not-every?", 11 | "vec", 12 | "min", 13 | "defn", 14 | "=", 15 | "list", 16 | "*", 17 | "next", 18 | "keys", 19 | "contains?", 20 | "<=", 21 | "if-not", 22 | "keep", 23 | "declare", 24 | "find", 25 | "dissoc", 26 | "not", 27 | "if-let", 28 | "update-in", 29 | "hash-map", 30 | "/", 31 | "reverse", 32 | "rand", 33 | "..", 34 | "every?", 35 | "-", 36 | "repeat", 37 | "true?", 38 | "iterate", 39 | "false?", 40 | "max", 41 | "cycle", 42 | "get-in", 43 | "nth", 44 | "comp", 45 | "partition", 46 | "fn", 47 | "for", 48 | "defrecord", 49 | "take", 50 | "rest", 51 | "identical?", 52 | "merge-with", 53 | "count", 54 | "empty?", 55 | "cons", 56 | "not-any?", 57 | "apply", 58 | "partition-by", 59 | "rem", 60 | "sorted-map", 61 | "distinct", 62 | "nil?", 63 | "map-indexed", 64 | "cond", 65 | "if", 66 | "let", 67 | "drop", 68 | "reify", 69 | "vals", 70 | "inc", 71 | "defmulti", 72 | "when-let", 73 | "not=", 74 | "doto", 75 | "this-as", 76 | "take-while", 77 | "or", 78 | "odd?", 79 | "case", 80 | "remove", 81 | "mapcat", 82 | ">=", 83 | "filter", 84 | "condp", 85 | "complement", 86 | "when", 87 | "neg?", 88 | "->", 89 | "and", 90 | "defmethod", 91 | "mod", 92 | "last", 93 | "<", 94 | "pos?", 95 | ".", 96 | "conj", 97 | "->>", 98 | "zero?", 99 | "set", 100 | "some", 101 | "juxt", 102 | "quot", 103 | "def", 104 | "reduce", 105 | "flatten", 106 | "repeatedly", 107 | "assoc-in", 108 | "assoc", 109 | "into", 110 | "when-not", 111 | "dec", 112 | "==", 113 | "get", 114 | "distinct?", 115 | "merge", 116 | "+", 117 | "list*", 118 | "even?", 119 | "letfn", 120 | "ns", 121 | "first", 122 | "deftype", 123 | ">" 124 | ]; 125 | -------------------------------------------------------------------------------- /spec/cljs-doc-spec.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import CljsDoc from '../lib/cljs-doc'; 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('CljsDoc', () => { 11 | let workspaceElement, activationPromise; 12 | 13 | beforeEach(() => { 14 | workspaceElement = atom.views.getView(atom.workspace); 15 | activationPromise = atom.packages.activatePackage('cljs-doc'); 16 | }); 17 | 18 | describe('when the cljs-doc: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('.cljs-doc')).not.toExist(); 23 | 24 | // This is an activation event, triggering it will cause the package to be 25 | // activated. 26 | atom.commands.dispatch(workspaceElement, 'cljs-doc:toggle'); 27 | 28 | waitsForPromise(() => { 29 | return activationPromise; 30 | }); 31 | 32 | runs(() => { 33 | expect(workspaceElement.querySelector('.cljs-doc')).toExist(); 34 | 35 | let cljsDocElement = workspaceElement.querySelector('.cljs-doc'); 36 | expect(cljsDocElement).toExist(); 37 | 38 | let cljsDocPanel = atom.workspace.panelForItem(cljsDocElement); 39 | expect(cljsDocPanel.isVisible()).toBe(true); 40 | atom.commands.dispatch(workspaceElement, 'cljs-doc:toggle'); 41 | expect(cljsDocPanel.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('.cljs-doc')).not.toExist(); 55 | 56 | // This is an activation event, triggering it causes the package to be 57 | // activated. 58 | atom.commands.dispatch(workspaceElement, 'cljs-doc:toggle'); 59 | 60 | waitsForPromise(() => { 61 | return activationPromise; 62 | }); 63 | 64 | runs(() => { 65 | // Now we can test for view visibility 66 | let cljsDocElement = workspaceElement.querySelector('.cljs-doc'); 67 | expect(cljsDocElement).toBeVisible(); 68 | atom.commands.dispatch(workspaceElement, 'cljs-doc:toggle'); 69 | expect(cljsDocElement).not.toBeVisible(); 70 | }); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /lib/cljs-doc-view.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import { Disposable } from 'atom'; 4 | import shell from 'shell'; 5 | import cljs from 'clojurescript'; 6 | import vars from './vars'; 7 | 8 | const exts = ['.clj', '.cljs', '.cljc']; 9 | const fileTypes = ['clj', 'cljs', 'cljc']; 10 | 11 | function shouldEnable(editor) { 12 | if (editor) { 13 | const filename = editor.getPath; 14 | const ftypes = editor.getGrammar().fileTypes; 15 | return (typeof filename === 'string' && 16 | exts.some((ext) => filename.endsWith(ext))) || 17 | fileTypes.some((ft) => ftypes.some((ftt) => ftt === ft)); 18 | } else { 19 | return false; 20 | } 21 | } 22 | 23 | class CljsDocView extends HTMLElement { 24 | 25 | initialize() { 26 | 27 | this.viewUpdatePending = false; 28 | 29 | this.classList.add('cljs-doc', 'inline-block'); 30 | 31 | this.style.display = 'none'; 32 | 33 | this.docEl = document.createElement('a'); 34 | this.docEl.classList.add('inline-block'); 35 | this.docEl.href = '#'; 36 | 37 | this.appendChild(this.docEl); 38 | 39 | this.activeItemSubscription = atom.workspace.onDidChangeActivePaneItem(() => { 40 | this.subscribeToActiveTextEditor(); 41 | }); 42 | 43 | this.subscribeToActiveTextEditor(); 44 | 45 | const clickHandler = () => this.showDoc(); 46 | 47 | this.addEventListener('click', clickHandler); 48 | this.clickSubscription = new Disposable(() => this.removeEventListener('click', clickHandler)); 49 | } 50 | 51 | destroy() { 52 | 53 | this.activeItemSubscription.dispose(); 54 | this.clickSubscription.dispose(); 55 | 56 | if (this.cursorSubscription) { 57 | this.cursorSubscription.dispose(); 58 | } 59 | } 60 | 61 | subscribeToActiveTextEditor() { 62 | 63 | const activeEditor = this.getActiveTextEditor(); 64 | 65 | if (shouldEnable(activeEditor) === false) { 66 | this.toggleDocPanel(false); 67 | return; 68 | } 69 | 70 | if (this.cursorSubscription) { 71 | this.cursorSubscription.dispose(); 72 | } 73 | 74 | if (activeEditor) { 75 | 76 | this.cursorSubscription = activeEditor.onDidChangeCursorPosition(({ cursor }) => { 77 | 78 | if (this.viewUpdatePending === false && cursor === activeEditor.getLastCursor()) { 79 | 80 | this.viewUpdatePending = true; 81 | 82 | let word = activeEditor.getWordUnderCursor({ 83 | wordRegex: /[A-Za-z0-9\-!?.<>:\/*=+_]+/g 84 | }); 85 | 86 | if (typeof word === 'string') { 87 | word = word.trim(); 88 | } 89 | 90 | const isValid = vars.includes(word); 91 | 92 | if (isValid) { 93 | this.lastWord = word; 94 | this.toggleDocPanel(true, word); 95 | } else { 96 | this.toggleDocPanel(false); 97 | } 98 | 99 | this.viewUpdatePending = false; 100 | } 101 | }); 102 | } 103 | } 104 | 105 | getActiveTextEditor() { 106 | return atom.workspace.getActiveTextEditor(); 107 | } 108 | 109 | toggleDocPanel(show, symbol) { 110 | if (show) { 111 | this.style.display = 'inline-block'; 112 | this.docEl.textContent = `(doc ${symbol})`; 113 | } else { 114 | this.style.display = 'none'; 115 | this.docEl.textContent = ''; 116 | } 117 | } 118 | 119 | showDoc() { 120 | try { 121 | const doc = cljs.eval(`(doc ${this.lastWord})`); 122 | const scoll = doc.split('\n'); 123 | 124 | scoll.pop(); 125 | scoll.shift(); 126 | 127 | const link = `https://clojuredocs.org/clojure.core/${this.lastWord}`; 128 | 129 | atom.notifications.addInfo(`(doc ${this.lastWord})`, { 130 | detail: scoll.join('\n'), 131 | dismissable: true, 132 | buttons: [ 133 | { 134 | text: 'Read on ClojureDocs', 135 | onDidClick: () => shell.openExternal(link), 136 | } 137 | ], 138 | }); 139 | } catch (err) { 140 | 141 | } 142 | } 143 | 144 | } 145 | 146 | export default document.registerElement('cljs-doc', { 147 | prototype: CljsDocView.prototype, 148 | extends: 'div', 149 | }); 150 | --------------------------------------------------------------------------------