Jumpy: No Match! 😞 "})}(r));switch(ur(r.w)){case 0:return Un(b(Kn,t,$(r,{w:u})));case 1:return function(n){return p(n,Gn(d([Pn(n.p),Hn(n.t),Dn(n.T)])))}(_n($(r,{T:u})));default:return p(r,Vn)}case 1:return r.p?Un(Zn(Wn(r))):p(r,Vn);case 0:return Un(function(n){return Zn($(n,{p:!0}))}($(r,{S:n.a})));default:return Un(_n(r))}var i}),fr=t(function(n){return n}),ar=Q,or=function(n){return{$:0,a:n}},cr=P({aO:function(){return p({p:!1,w:"",S:l,T:"",t:""},Vn)},aV:function(){return ar(d([Fn(cn),Xn(on),Yn(fr(sn)),qn(fr(an))]))},aX:ir});fn={StateMachine:{init:cr(or({}))(0)}},n.Elm?function n(r,t){for(var u in t)u in r?"init"==u?w(6):n(r[u],t[u]):r[u]=t[u]}(n.Elm,fn):n.Elm=fn}(this);
--------------------------------------------------------------------------------
/dist/jumpy-view.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | // TODO: Merge in @johngeorgewright's code for treeview
5 | // TODO: Merge in @willdady's code for better accuracy.
6 | /* global atom */
7 | const atom_1 = require("atom");
8 | const _ = require("lodash");
9 | const words_1 = require("./labelers/words");
10 | const tabs_1 = require("./labelers/tabs");
11 | const tree_items_1 = require("./labelers/tree-items");
12 | const label_reducer_1 = require("./label-reducer");
13 | const keys_1 = require("./keys");
14 | const viewHelpers_1 = require("./viewHelpers");
15 | class JumpyView {
16 | constructor(serializedState, stateMachine) {
17 | this.workspaceElement = atom.views.getView(atom.workspace);
18 | this.disposables = new atom_1.CompositeDisposable();
19 | this.drawnLabels = [];
20 | this.commands = new atom_1.CompositeDisposable();
21 | this.stateMachine = stateMachine;
22 | this.setSettings();
23 | // Subscribe:
24 | this.stateMachine.ports.validKeyEntered.subscribe((keyLabel) => {
25 | for (const label of this.drawnLabels) {
26 | if (!label.keyLabel || !label.element) {
27 | continue;
28 | }
29 | if (!label.keyLabel.startsWith(keyLabel)) {
30 | label.element.classList.add('irrelevant');
31 | }
32 | }
33 | this.currentLabels = label_reducer_1.default(this.currentLabels, keyLabel);
34 | });
35 | this.stateMachine.ports.labelJumped.subscribe((keyLabel) => {
36 | _.find(this.currentLabels, (label) => label.keyLabel === keyLabel).jump();
37 | });
38 | this.stateMachine.ports.activeChanged.subscribe((active) => {
39 | this.active = active;
40 | if (!this.active) {
41 | this.turnOffListeners();
42 | this.clearJumpMode();
43 | }
44 | });
45 | this.stateMachine.ports.statusChanged.subscribe((statusMarkup) => {
46 | if (this.statusBarElement) {
47 | this.statusBarElement.innerHTML = statusMarkup;
48 | }
49 | });
50 | this.keydownListener = (event) => {
51 | // use the code property for testing if
52 | // the key is relevant to Jumpy
53 | // that is, that it's an alpha char.
54 | // use the key character to pass the exact key
55 | // that is, (upper or lower) to the state machine.
56 | // if jumpy catches it...stop the event propagation.
57 | const { code, key, metaKey, ctrlKey, altKey } = event;
58 | if (metaKey || ctrlKey || altKey) {
59 | return;
60 | }
61 | if (/^Key[A-Z]{1}$/.test(code)) {
62 | event.preventDefault();
63 | event.stopPropagation();
64 | this.stateMachine.ports.key.send(key.charCodeAt());
65 | }
66 | };
67 | this.commands.add(atom.commands.add('atom-workspace', {
68 | 'jumpy:toggle': () => { this.toggle(); },
69 | 'jumpy:reset': () => { this.reset(); },
70 | 'jumpy:clear': () => {
71 | this.stateMachine.ports.exit.send(null);
72 | }
73 | }));
74 | }
75 | initializeStatusBar() {
76 | // NOTE: This needs to be called when status bar is ready, so can't be called from constructor
77 | if (this.statusBarElement) {
78 | return;
79 | }
80 | const atomStatusBar = document.querySelector('status-bar');
81 | if (atomStatusBar) {
82 | const statusBarElement = document.createElement('div');
83 | this.statusBarElement = statusBarElement;
84 | statusBarElement.id = 'status-bar-jumpy-container';
85 | statusBarElement.classList.add('inline-block');
86 | statusBarElement.innerHTML = "Jumpy: Jump Mode!
";
87 | atomStatusBar.addLeftTile({
88 | item: statusBarElement,
89 | priority: -1
90 | });
91 | }
92 | }
93 | setSettings() {
94 | let fontSize = atom.config.get('jumpy.fontSize');
95 | if (isNaN(fontSize) || fontSize > 1) {
96 | fontSize = .75; // default
97 | }
98 | const fontSizeString = `${fontSize * 100}%`;
99 | this.settings = {
100 | fontSize: fontSizeString,
101 | highContrast: atom.config.get('jumpy.highContrast'),
102 | wordsPattern: new RegExp(atom.config.get('jumpy.matchPattern'), 'g')
103 | };
104 | }
105 | reset() {
106 | this.currentLabels = _.clone(this.allLabels);
107 | for (const label of this.currentLabels) {
108 | if (label.element) {
109 | label.element.classList.remove('irrelevant');
110 | }
111 | }
112 | this.stateMachine.ports.reset.send(null);
113 | }
114 | loadLabels() {
115 | const environment = {
116 | keys: keys_1.getKeySet(atom.config.get('jumpy.customKeys')),
117 | settings: this.settings
118 | };
119 | // TODO: reduce with concat all labelers -> labeler.getLabels()
120 | const wordLabels = words_1.default(environment);
121 | const tabLabels = tabs_1.default(environment);
122 | const treeItemLabels = tree_items_1.default(environment);
123 | // TODO: I really think alllabels can just be drawnlabels
124 | // maybe I call labeler.draw() still returns back anyway? Less functional?
125 | this.allLabels = [
126 | ...wordLabels,
127 | ...tabLabels,
128 | ...treeItemLabels
129 | ];
130 | for (const label of this.allLabels) {
131 | this.drawnLabels.push(label.drawLabel());
132 | }
133 | this.currentLabels = _.clone(this.allLabels);
134 | this.stateMachine.ports.getLabels.send(this.currentLabels
135 | .filter((label) => label.keyLabel) // ie. tabs open after limit reached
136 | .map((label) => label.keyLabel));
137 | }
138 | toggle() {
139 | if (!this.active) {
140 | this.loadLabels();
141 | this.initializeStatusBar();
142 | this.turnOnListeners();
143 | }
144 | else {
145 | this.stateMachine.ports.exit.send(null);
146 | }
147 | }
148 | turnOnListeners() {
149 | this.workspaceElement.addEventListener('keydown', this.keydownListener, true);
150 | for (const e of ['blur', 'click', 'scroll']) {
151 | this.workspaceElement.addEventListener(e, () => this.clearJumpModeHandler(), true);
152 | }
153 | }
154 | turnOffListeners() {
155 | this.workspaceElement.removeEventListener('keydown', this.keydownListener, true);
156 | for (const e of ['blur', 'click', 'scroll']) {
157 | this.workspaceElement.removeEventListener(e, () => this.clearJumpModeHandler(), true);
158 | }
159 | }
160 | clearJumpModeHandler() {
161 | this.stateMachine.ports.exit.send(null);
162 | this.clearJumpMode();
163 | }
164 | clearJumpMode() {
165 | const clearAllLabels = () => {
166 | for (const label of this.drawnLabels) {
167 | label.destroy();
168 | }
169 | this.drawnLabels = []; // Very important for GC.
170 | // Verifiable in Dev Tools -> Timeline -> Nodes.
171 | };
172 | this.allLabels = [];
173 | const treeView = document.getElementsByClassName('tree-view');
174 | if (treeView.length) {
175 | viewHelpers_1.removeJumpModeClasses(treeView[0]);
176 | }
177 | for (const editor of atom.workspace.getTextEditors()) {
178 | const editorView = atom.views.getView(editor);
179 | viewHelpers_1.removeJumpModeClasses(editorView);
180 | }
181 | clearAllLabels();
182 | if (this.disposables) {
183 | this.disposables.dispose();
184 | }
185 | }
186 | // Returns an object that can be retrieved when package is activated
187 | serialize() { }
188 | // Tear down any state and detach
189 | destroy() {
190 | if (this.commands) {
191 | this.commands.dispose();
192 | }
193 | this.clearJumpMode();
194 | }
195 | }
196 | exports.default = JumpyView;
197 | //# sourceMappingURL=jumpy-view.js.map
--------------------------------------------------------------------------------
/dist/jumpy-view.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"jumpy-view.js","sourceRoot":"","sources":["../lib/jumpy-view.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/jumpy.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const elmApp = require("../dist/elm/StateMachine");
5 | const jumpy_view_1 = require("./jumpy-view");
6 | module.exports = {
7 | jumpyView: null,
8 | config: {
9 | fontSize: {
10 | description: 'The font size of jumpy labels.',
11 | type: 'number',
12 | default: .75,
13 | minimum: 0,
14 | maximum: 1
15 | },
16 | highContrast: {
17 | description: 'This will display a high contrast label, \
18 | usually green. It is dynamic per theme.',
19 | type: 'boolean',
20 | default: false
21 | },
22 | useHomingBeaconEffectOnJumps: {
23 | description: 'This will animate a short lived homing beacon upon \
24 | jump.',
25 | type: 'boolean',
26 | default: true
27 | },
28 | matchPattern: {
29 | description: 'Jumpy will create labels based on this pattern.',
30 | type: 'string',
31 | default: '([A-Z]+([0-9a-z])*)|[a-z0-9]{2,}'
32 | },
33 | customKeys: {
34 | description: 'Jumpy will use these characters in the specifed order to create labels (comma separated)',
35 | type: 'array',
36 | default: []
37 | }
38 | },
39 | activate(state) {
40 | const stateMachine = elmApp.Elm.StateMachine.init();
41 | this.jumpyView = new jumpy_view_1.default(state.jumpyViewState, stateMachine);
42 | },
43 | deactivate() {
44 | if (this.jumpyView) {
45 | this.jumpyView.destroy();
46 | }
47 | this.jumpyView = null;
48 | },
49 | serialize() {
50 | return {
51 | jumpyViewState: this.jumpyView.serialize(),
52 | };
53 | }
54 | };
55 | //# sourceMappingURL=jumpy.js.map
--------------------------------------------------------------------------------
/dist/jumpy.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"jumpy.js","sourceRoot":"","sources":["../lib/jumpy.ts"],"names":[],"mappings":";AAAA,WAAW,CAAC;;AAEZ,mDAAmD;AACnD,6CAAqC;AAErC,MAAM,CAAC,OAAO,GAAG;IAEb,SAAS,EAAE,IAAI;IACf,MAAM,EAAE;QACJ,QAAQ,EAAE;YACN,WAAW,EAAE,gCAAgC;YAC7C,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,GAAG;YACZ,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;SACb;QACD,YAAY,EAAE;YACV,WAAW,EAAE;qDAC4B;YACzC,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;SACjB;QACD,4BAA4B,EAAE;YAC1B,WAAW,EAAE;kBACP;YACN,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;SAChB;QACD,YAAY,EAAE;YACV,WAAW,EAAE,iDAAiD;YAC9D,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,kCAAkC;SAC9C;QACD,UAAU,EAAE;YACV,WAAW,EAAE,0FAA0F;YACvG,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,EAAE;SACZ;KACJ;IAED,QAAQ,CAAC,KAAU;QACf,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,SAAS,GAAG,IAAI,oBAAS,CAAC,KAAK,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACvE,CAAC;IAED,UAAU;QACN,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YACjB,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,SAAS;QACL,MAAM,CAAC;YACH,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;SAC7C,CAAC;IACN,CAAC;CACJ,CAAC"}
--------------------------------------------------------------------------------
/dist/keys.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const _ = require("lodash");
5 | function getKeySet(customKeys) {
6 | let lowerCharacters = [];
7 | let upperCharacters = [];
8 | if (!customKeys.length) {
9 | lowerCharacters = _.range('a'.charCodeAt(0), 'z'.charCodeAt(0) + 1 /* for inclusive*/)
10 | .map(c => String.fromCharCode(c));
11 | upperCharacters = _.range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1 /* for inclusive*/)
12 | .map(c => String.fromCharCode(c));
13 | }
14 | else {
15 | for (let key of customKeys) {
16 | lowerCharacters.push(key.toLowerCase());
17 | upperCharacters.push(key.toUpperCase());
18 | }
19 | }
20 | const keys = [];
21 | // A little ugly.
22 | // I used itertools.permutation in python.
23 | // Couldn't find a good one in npm. Don't worry this takes < 1ms once.
24 | // TODO: try a zip? and or make a func
25 | for (let c1 of lowerCharacters) {
26 | for (let c2 of lowerCharacters) {
27 | keys.push(c1 + c2);
28 | }
29 | }
30 | for (let c1 of upperCharacters) {
31 | for (let c2 of lowerCharacters) {
32 | keys.push(c1 + c2);
33 | }
34 | }
35 | for (let c1 of lowerCharacters) {
36 | for (let c2 of upperCharacters) {
37 | keys.push(c1 + c2);
38 | }
39 | }
40 | // TODO: use TS's ReadonlyArray?
41 | return keys;
42 | }
43 | exports.getKeySet = getKeySet;
44 | //# sourceMappingURL=keys.js.map
--------------------------------------------------------------------------------
/dist/keys.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"keys.js","sourceRoot":"","sources":["../lib/keys.ts"],"names":[],"mappings":";AAAA,WAAW,CAAC;;AAEZ,4BAA4B;AAE5B,mBAA0B,UAAyB;IAC/C,IAAI,eAAe,GAAkB,EAAE,CAAC;IACxC,IAAI,eAAe,GAAkB,EAAE,CAAC;IAExC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACrB,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC;aACjF,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC;aACjF,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC;YACzB,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;YACxC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAkB,EAAE,CAAC;IAE/B,iBAAiB;IACjB,0CAA0C;IAC1C,uEAAuE;IACvE,sCAAsC;IACtC,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC;QAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;IACD,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC;QAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;IACD,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC;QAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;IAED,gCAAgC;IAChC,MAAM,CAAC,IAAI,CAAC;AAChB,CAAC;AAxCD,8BAwCC"}
--------------------------------------------------------------------------------
/dist/label-interface.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | //# sourceMappingURL=label-interface.js.map
--------------------------------------------------------------------------------
/dist/label-interface.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"label-interface.js","sourceRoot":"","sources":["../lib/label-interface.ts"],"names":[],"mappings":""}
--------------------------------------------------------------------------------
/dist/label-reducer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | // (PURE FUNCTION)
5 | //
6 | // WHEN GIVEN:
7 | //
8 | // 1. AN ARRAY OF LABELS (* SEE BELOW)
9 | // (and)
10 | // 2. A NEW INPUT KEY
11 | //
12 | // RETURNS new collection of labels
13 | // *without* the labels that do not start with the current key
14 | function labelReducer(labels, currentKey) {
15 | return labels.filter(function (label) {
16 | if (!label.keyLabel) {
17 | return false;
18 | }
19 | return label.keyLabel.startsWith(currentKey);
20 | });
21 | }
22 | exports.default = labelReducer;
23 | //# sourceMappingURL=label-reducer.js.map
--------------------------------------------------------------------------------
/dist/label-reducer.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"label-reducer.js","sourceRoot":"","sources":["../lib/label-reducer.ts"],"names":[],"mappings":";AAAA,WAAW,CAAC;;AAIZ,kBAAkB;AAClB,EAAE;AACF,cAAc;AACd,EAAE;AACF,4CAA4C;AAC5C,aAAa;AACb,0BAA0B;AAC1B,EAAE;AACF,mCAAmC;AACnC,8DAA8D;AAE9D,sBAAsC,MAAoB,EAAE,UAAmB;IAC3E,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAS,KAAa;QACvC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClB,MAAM,CAAC,KAAK,CAAC;QACjB,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACP,CAAC;AAPD,+BAOC"}
--------------------------------------------------------------------------------
/dist/labelers/tabs.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const atom_1 = require("atom");
5 | class TabLabel {
6 | destroy() {
7 | if (this.element) {
8 | this.element.remove();
9 | }
10 | }
11 | drawLabel() {
12 | const tabsPane = atom.workspace.paneForItem(this.textEditor);
13 | const tabsPaneElement = atom.views.getView(tabsPane);
14 | const foundTab = tabsPaneElement
15 | .querySelector(`[data-path='${this.textEditor.getPath()}']`);
16 | if (!foundTab) {
17 | return this;
18 | }
19 | const labelElement = document.createElement('div');
20 | if (this.keyLabel) {
21 | labelElement.textContent = this.keyLabel;
22 | }
23 | labelElement.style.position = 'fixed';
24 | labelElement.classList.add('jumpy-label'); // For styling and tests
25 | labelElement.classList.add('tab-label');
26 | labelElement.style.fontSize = this.settings.fontSize;
27 | if (this.settings.highContrast) {
28 | labelElement.classList.add('high-contrast');
29 | }
30 | this.element = labelElement;
31 | foundTab.appendChild(labelElement);
32 | return this;
33 | }
34 | animateBeacon() {
35 | // TODO: abstract function to find tab!
36 | const tabsPane = atom.workspace.paneForItem(this.textEditor);
37 | const tabsPaneElement = atom.views.getView(tabsPane);
38 | const foundTab = tabsPaneElement
39 | .querySelector(`[data-path='${this.textEditor.getPath()}'`);
40 | if (foundTab) {
41 | const beacon = document.createElement('span');
42 | beacon.style.position = 'relative';
43 | beacon.style.zIndex = '4';
44 | beacon.classList.add('beacon'); // For styling and tests
45 | beacon.classList.add('tab-beacon');
46 | foundTab.appendChild(beacon);
47 | setTimeout(function () {
48 | beacon.remove();
49 | }, 150);
50 | }
51 | }
52 | jump() {
53 | const pane = atom.workspace.paneForItem(this.textEditor);
54 | pane.activate();
55 | pane.activateItem(this.textEditor);
56 | if (atom.config.get('jumpy.useHomingBeaconEffectOnJumps')) {
57 | this.animateBeacon();
58 | }
59 | }
60 | }
61 | const labeler = function (env) {
62 | const labels = [];
63 | for (const textEditor of atom.workspace.getPaneItems()) {
64 | if (!(textEditor instanceof atom_1.TextEditor) || !textEditor.buffer) {
65 | continue;
66 | }
67 | const keyLabel = env.keys.shift();
68 | const label = new TabLabel();
69 | label.settings = env.settings;
70 | label.keyLabel = keyLabel;
71 | label.textEditor = textEditor;
72 | labels.push(label);
73 | }
74 | return labels;
75 | };
76 | exports.default = labeler;
77 | //# sourceMappingURL=tabs.js.map
--------------------------------------------------------------------------------
/dist/labelers/tabs.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../lib/labelers/tabs.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/labelers/tree-items.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const viewHelpers_1 = require("../viewHelpers");
5 | class TreeItemLabel {
6 | destroy() {
7 | if (this.element) {
8 | this.element.remove();
9 | }
10 | }
11 | drawLabel() {
12 | const labelElement = document.createElement('div');
13 | if (this.keyLabel) {
14 | labelElement.textContent = this.keyLabel;
15 | }
16 | labelElement.style.position = 'absolute';
17 | labelElement.classList.add('jumpy-label'); // For styling and tests
18 | labelElement.classList.add('tree-item-label');
19 | labelElement.style.fontSize = this.settings.fontSize;
20 | if (this.settings.highContrast) {
21 | labelElement.classList.add('high-contrast');
22 | }
23 | this.element = labelElement;
24 | const parent = this.item.parentElement;
25 | if (parent) {
26 | parent.appendChild(labelElement);
27 | }
28 | return this;
29 | }
30 | animateBeacon() {
31 | const beacon = document.createElement('span');
32 | beacon.style.position = 'relative';
33 | beacon.style.zIndex = '4';
34 | beacon.classList.add('beacon'); // For styling and tests
35 | beacon.classList.add('tree-item-beacon');
36 | this.item.appendChild(beacon);
37 | setTimeout(function () {
38 | beacon.remove();
39 | }, 150);
40 | }
41 | jump() {
42 | function triggerMouseEvent(element, eventType) {
43 | var clickEvent = new MouseEvent("click", {
44 | bubbles: true,
45 | cancelable: true,
46 | view: window
47 | });
48 | element.dispatchEvent(clickEvent);
49 | }
50 | const treeItem = this.item.parentElement;
51 | if (treeItem) {
52 | // TODO: use only 1 of these with a guard statement.
53 | atom.commands.dispatch(treeItem, 'tree-view:toggle-focus');
54 | triggerMouseEvent(treeItem, 'mousedown');
55 | atom.commands.dispatch(treeItem, 'tree-view:toggle-focus');
56 | }
57 | // this.item.parentElement.click();
58 | if (atom.config.get('jumpy.useHomingBeaconEffectOnJumps')) {
59 | this.animateBeacon();
60 | }
61 | }
62 | }
63 | const labeler = function (env) {
64 | const labels = [];
65 | // just here for hotkey cascading:
66 | const treeView = document.querySelector('.tree-view');
67 | if (treeView) {
68 | viewHelpers_1.addJumpModeClasses(treeView);
69 | }
70 | const treeItems = document.querySelectorAll('.tree-view-root .directory .list-item .name');
71 | for (const treeItem of treeItems) {
72 | const keyLabel = env.keys.shift();
73 | const label = new TreeItemLabel();
74 | label.settings = env.settings;
75 | label.keyLabel = keyLabel;
76 | label.item = treeItem;
77 | labels.push(label);
78 | }
79 | return labels;
80 | };
81 | exports.default = labeler;
82 | //# sourceMappingURL=tree-items.js.map
--------------------------------------------------------------------------------
/dist/labelers/tree-items.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"tree-items.js","sourceRoot":"","sources":["../../lib/labelers/tree-items.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/labelers/words.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | 'use babel';
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const _ = require("lodash");
5 | const viewHelpers_1 = require("../viewHelpers");
6 | const atom_1 = require("atom");
7 | function getVisibleColumnRange(editorView) {
8 | const charWidth = editorView.getDefaultCharacterWidth();
9 | // FYI: asserts:
10 | // numberOfVisibleColumns = editorView.getWidth() / charWidth
11 | const minColumn = (editorView.getScrollLeft() / charWidth) - 1;
12 | const maxColumn = editorView.getScrollRight() / charWidth;
13 | return [
14 | minColumn,
15 | maxColumn
16 | ];
17 | }
18 | // Taken from jQuery: https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
19 | function isVisible(element) {
20 | return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
21 | }
22 | class WordLabel {
23 | destroy() {
24 | this.marker.destroy();
25 | }
26 | drawLabel() {
27 | const { textEditor, lineNumber, column, keyLabel } = this;
28 | this.marker = textEditor.markScreenRange(new atom_1.Range(new atom_1.Point(lineNumber, column), new atom_1.Point(lineNumber, column)), { invalidate: 'touch' });
29 | const labelElement = document.createElement('div');
30 | labelElement.textContent = keyLabel;
31 | labelElement.style.fontSize = this.settings.fontSize;
32 | labelElement.classList.add('jumpy-label'); // For styling and tests
33 | if (this.settings.highContrast) {
34 | labelElement.classList.add('high-contrast');
35 | }
36 | const decoration = textEditor.decorateMarker(this.marker, {
37 | type: 'overlay',
38 | item: labelElement,
39 | position: 'head'
40 | });
41 | this.element = labelElement;
42 | return this;
43 | }
44 | animateBeacon(input) {
45 | const position = input;
46 | const range = atom_1.Range(position, position);
47 | const marker = this.textEditor.markScreenRange(range, { invalidate: 'never' });
48 | const beacon = document.createElement('span');
49 | beacon.classList.add('beacon'); // For styling and tests
50 | this.textEditor.decorateMarker(marker, {
51 | item: beacon,
52 | type: 'overlay'
53 | });
54 | setTimeout(function () {
55 | marker.destroy();
56 | }, 150);
57 | }
58 | jump() {
59 | const currentEditor = this.textEditor;
60 | const editorView = atom.views.getView(currentEditor);
61 | // TODO: pretty sure this can't be useful...anymore
62 | // I think it had somethign to do with the observers etc.
63 | // Prevent other editors from jumping cursors as well
64 | // TODO: make a test for this if return
65 | if (currentEditor.id !== this.textEditor.id) {
66 | return;
67 | }
68 | const pane = atom.workspace.paneForItem(currentEditor);
69 | pane.activate();
70 | // isVisualMode is for vim-mode or vim-mode-plus:
71 | const isVisualMode = editorView.classList.contains('visual-mode');
72 | // isSelected is for regular selection in atom or in insert-mode in vim
73 | const isSelected = (currentEditor.getSelections().length === 1 &&
74 | currentEditor.getSelectedText() !== '');
75 | const position = atom_1.Point(this.lineNumber, this.column);
76 | if (isVisualMode || isSelected) {
77 | currentEditor.selectToScreenPosition(position);
78 | }
79 | else {
80 | currentEditor.setCursorScreenPosition(position);
81 | }
82 | if (atom.config.get('jumpy.useHomingBeaconEffectOnJumps')) {
83 | this.animateBeacon(position);
84 | }
85 | }
86 | }
87 | const labeler = function (env) {
88 | const labels = [];
89 | env.settings.wordsPattern.lastIndex = 0; // reset the RegExp for subsequent calls.
90 | for (const textEditor of atom.workspace.getTextEditors()) {
91 | const editorView = atom.views.getView(textEditor);
92 | // 'jumpy-jump-mode is for keymaps and utilized by tests
93 | viewHelpers_1.addJumpModeClasses(editorView);
94 | // current labels for current textEditor in loop.
95 | if (!env.keys.length) {
96 | continue;
97 | }
98 | const [minColumn, maxColumn] = getVisibleColumnRange(editorView);
99 | const rows = textEditor.getVisibleRowRange();
100 | if (!rows || !isVisible(editorView)) {
101 | continue;
102 | }
103 | const [firstVisibleRow, lastVisibleRow] = rows;
104 | // TODO: Right now there are issues with lastVisbleRow
105 | for (const lineNumber of _.range(firstVisibleRow, lastVisibleRow) /*excludes end value*/) {
106 | const lineContents = textEditor.lineTextForScreenRow(lineNumber);
107 | if (textEditor.isFoldedAtScreenRow(lineNumber)) {
108 | if (!env.keys.length) {
109 | continue; // try continue?
110 | }
111 | const keyLabel = env.keys.shift();
112 | const label = new WordLabel();
113 | label.settings = env.settings;
114 | label.textEditor = textEditor;
115 | label.keyLabel = keyLabel;
116 | label.lineNumber = lineNumber;
117 | label.column = 0;
118 | labels.push(label);
119 | }
120 | else {
121 | let word;
122 | while ((word = env.settings.wordsPattern.exec(lineContents)) != null && env.keys.length) {
123 | const keyLabel = env.keys.shift();
124 | const column = word.index;
125 | // Do not do anything... markers etc.
126 | // if the columns are out of bounds...
127 | if (column > minColumn && column < maxColumn) {
128 | const label = new WordLabel();
129 | label.settings = env.settings;
130 | label.textEditor = textEditor;
131 | label.keyLabel = keyLabel;
132 | label.lineNumber = lineNumber;
133 | label.column = column;
134 | labels.push(label);
135 | }
136 | }
137 | }
138 | } // end: each line
139 | } // end: for each textEditor
140 | return labels;
141 | };
142 | exports.default = labeler;
143 | //# sourceMappingURL=words.js.map
--------------------------------------------------------------------------------
/dist/labelers/words.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"words.js","sourceRoot":"","sources":["../../lib/labelers/words.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/viewHelpers.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | function addJumpModeClasses(element) {
4 | element.classList.add('jumpy-jump-mode', 'jumpy-more-specific1', 'jumpy-more-specific2');
5 | }
6 | exports.addJumpModeClasses = addJumpModeClasses;
7 | function removeJumpModeClasses(element) {
8 | element.classList.remove('jumpy-jump-mode', 'jumpy-more-specific1', 'jumpy-more-specific2');
9 | }
10 | exports.removeJumpModeClasses = removeJumpModeClasses;
11 | //# sourceMappingURL=viewHelpers.js.map
--------------------------------------------------------------------------------
/dist/viewHelpers.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"viewHelpers.js","sourceRoot":"","sources":["../lib/viewHelpers.ts"],"names":[],"mappings":";;AAAA,4BAAmC,OAAoB;IACnD,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,EACnC,sBAAsB,EAAE,sBAAsB,CAAC,CAAC;AACxD,CAAC;AAHD,gDAGC;AAED,+BAAsC,OAAoB;IACtD,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,iBAAiB,EACtC,sBAAsB,EAAE,sBAAsB,CAAC,CAAC;AACxD,CAAC;AAHD,sDAGC"}
--------------------------------------------------------------------------------
/elm.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "application",
3 | "source-directories": [
4 | "lib"
5 | ],
6 | "elm-version": "0.19.0",
7 | "dependencies": {
8 | "direct": {
9 | "elm/browser": "1.0.0",
10 | "elm/core": "1.0.0",
11 | "elm/html": "1.0.0",
12 | "elm/json": "1.0.0"
13 | },
14 | "indirect": {
15 | "elm/time": "1.0.0",
16 | "elm/url": "1.0.0",
17 | "elm/virtual-dom": "1.0.0",
18 | "elm/random": "1.0.0"
19 | }
20 | },
21 | "test-dependencies": {
22 | "direct": {
23 | "elm-explorations/test": "1.0.0"
24 | },
25 | "indirect": {}
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/keymaps/jumpy.cson:
--------------------------------------------------------------------------------
1 | # Keybindings require three things to be fully defined: A selector that is
2 | # matched against the focused element, the keystroke and the command to
3 | # execute.
4 | #
5 | # Below is a basic keybinding which registers on all platforms by applying to
6 | # the root workspace element.
7 |
8 | # For more detailed documentation see
9 | # https://atom.io/docs/latest/advanced/keymaps
10 | 'atom-workspace atom-text-editor:not(.mini),
11 | .tree-view':
12 | 'shift-enter': 'jumpy:toggle'
13 |
14 | 'atom-workspace atom-text-editor.jumpy-jump-mode.jumpy-more-specific1.jumpy-more-specific2,
15 | .tree-view.jumpy-jump-mode.jumpy-more-specific1.jumpy-more-specific2':
16 | 'backspace': 'jumpy:reset'
17 | 'enter': 'jumpy:clear'
18 | 'space': 'jumpy:clear'
19 | 'escape': 'jumpy:clear'
20 | 'shift-enter': 'jumpy:clear'
21 |
--------------------------------------------------------------------------------
/lib/StateMachine.elm:
--------------------------------------------------------------------------------
1 | port module StateMachine exposing (Flags, Labels, Model, Msg(..), activeChanged, addKeyToStatus, clearStatus, exit, getLabels, init, key, labelJumped, main, modelAndJumped, modelAndStatus, onKeyPress, reset, resetKeys, resetStatus, setNoMatchStatus, statusChanged, turnOff, turnOn, update, validKeyEntered)
2 |
3 | import Char
4 | import Html as Html exposing (..)
5 | import Html.Events as Events exposing (..)
6 | import Json.Decode as Json
7 | import List exposing (any)
8 | import String exposing (..)
9 |
10 |
11 | main : Program Flags Model Msg
12 | main =
13 | Platform.worker
14 | { init = init
15 | , update = update
16 | , subscriptions =
17 | \model ->
18 | Sub.batch
19 | [ getLabels LoadLabels
20 | , key KeyEntered
21 | , reset (Basics.always Reset)
22 | , exit (Basics.always Exit)
23 | ]
24 | }
25 |
26 |
27 | type alias Labels =
28 | List String
29 |
30 |
31 |
32 | -- Outbound
33 |
34 |
35 | port activeChanged : Bool -> Cmd msg
36 |
37 |
38 | port statusChanged : String -> Cmd msg
39 |
40 |
41 | port validKeyEntered : String -> Cmd msg
42 |
43 |
44 | port labelJumped : String -> Cmd msg
45 |
46 |
47 |
48 | -- Inbound
49 |
50 |
51 | port getLabels : (Labels -> msg) -> Sub msg
52 |
53 |
54 | port key : (Int -> msg) -> Sub msg
55 |
56 |
57 | port reset : (() -> msg) -> Sub msg
58 |
59 |
60 | port exit : (() -> msg) -> Sub msg
61 |
62 |
63 | type Msg
64 | = LoadLabels Labels
65 | | Reset
66 | | KeyEntered Int
67 | | Exit
68 |
69 |
70 | type alias Model =
71 | { active : Bool
72 | , keysEntered : String
73 | , lastJumped : String
74 | , labels : Labels
75 | , status : String
76 | }
77 |
78 |
79 | type alias Flags =
80 | {}
81 |
82 |
83 | init : Flags -> ( Model, Cmd Msg )
84 | init flags =
85 | ( { active = False
86 | , keysEntered = ""
87 | , lastJumped = ""
88 | , labels = []
89 | , status = ""
90 | }
91 | , Cmd.none
92 | )
93 |
94 |
95 | onKeyPress : (Int -> msg) -> Attribute msg
96 | onKeyPress tagger =
97 | on "keypress" (Json.map tagger keyCode)
98 |
99 |
100 | clearStatus : Model -> Model
101 | clearStatus model =
102 | { model | status = "" }
103 |
104 |
105 | resetStatus : Model -> Model
106 | resetStatus model =
107 | { model | status = "Jumpy: Jump Mode!
" }
108 |
109 |
110 | resetKeys : Model -> Model
111 | resetKeys model =
112 | { model | keysEntered = "" }
113 |
114 |
115 | turnOff : Model -> Model
116 | turnOff model =
117 | { model | active = False }
118 | |> resetKeys
119 | |> clearStatus
120 |
121 |
122 | setNoMatchStatus : Model -> Model
123 | setNoMatchStatus model =
124 | { model | status = "Jumpy: No Match! 😞
" }
125 |
126 |
127 | addKeyToStatus : String -> Model -> Model
128 | addKeyToStatus keyEntered model =
129 | { model | status = "Jumpy: " ++ keyEntered ++ "
" }
130 |
131 |
132 | modelAndStatus : Model -> ( Model, Cmd Msg )
133 | modelAndStatus model =
134 | ( model
135 | , Cmd.batch
136 | [ activeChanged model.active
137 | , statusChanged model.status
138 | , validKeyEntered model.keysEntered
139 | ]
140 | )
141 |
142 |
143 | modelAndJumped : Model -> ( Model, Cmd Msg )
144 | modelAndJumped model =
145 | ( model
146 | , Cmd.batch
147 | [ activeChanged model.active
148 | , statusChanged model.status
149 | , labelJumped model.lastJumped
150 | ]
151 | )
152 |
153 |
154 | turnOn : Model -> Model
155 | turnOn model =
156 | { model | active = True }
157 | |> resetStatus
158 |
159 |
160 | update : Msg -> Model -> ( Model, Cmd Msg )
161 | update msg model =
162 | case msg of
163 | KeyEntered keyCode ->
164 | let
165 | keyEntered =
166 | keyCode |> Char.fromCode |> String.fromChar
167 |
168 | newKeysEntered =
169 | model.keysEntered ++ keyEntered
170 |
171 | keysEnteredMatch =
172 | model.labels
173 | |> List.any (\label -> startsWith newKeysEntered label)
174 | in
175 | if model.active then
176 | if not keysEnteredMatch then
177 | model
178 | |> setNoMatchStatus
179 | |> modelAndStatus
180 |
181 | else
182 | case length model.keysEntered of
183 | 0 ->
184 | -- FIRST LETTER ----------
185 | { model | keysEntered = newKeysEntered }
186 | |> addKeyToStatus keyEntered
187 | |> modelAndStatus
188 |
189 | 1 ->
190 | -- SECOND LETTER ----------
191 | { model | lastJumped = newKeysEntered }
192 | |> turnOff
193 | |> modelAndJumped
194 |
195 | _ ->
196 | ( model, Cmd.none )
197 |
198 | else
199 | ( model, Cmd.none )
200 |
201 | Reset ->
202 | if model.active then
203 | model
204 | |> resetKeys
205 | |> resetStatus
206 | |> modelAndStatus
207 |
208 | else
209 | ( model, Cmd.none )
210 |
211 | LoadLabels labels ->
212 | { model | labels = labels }
213 | |> turnOn
214 | |> modelAndStatus
215 |
216 | Exit ->
217 | model
218 | |> turnOff
219 | |> modelAndStatus
220 |
--------------------------------------------------------------------------------
/lib/jumpy-view.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | // TODO: Merge in @johngeorgewright's code for treeview
4 | // TODO: Merge in @willdady's code for better accuracy.
5 |
6 | /* global atom */
7 | import { CompositeDisposable, Point } from 'atom';
8 | import * as _ from 'lodash';
9 |
10 | import { LabelEnvironment, Label } from './label-interface';
11 | import getWordLabels from './labelers/words';
12 | import getTabLabels from './labelers/tabs';
13 | import getTreeItemLabels from './labelers/tree-items';
14 | import labelReducer from './label-reducer';
15 | import { getKeySet } from './keys';
16 | import { removeJumpModeClasses } from './viewHelpers';
17 |
18 | export default class JumpyView {
19 | workspaceElement: any;
20 | disposables: CompositeDisposable;
21 | commands: CompositeDisposable;
22 | stateMachine: any;
23 | active: boolean;
24 | allLabels: Array; // TODO: these lists of labels seem a little much.
25 | currentLabels: Array;
26 | drawnLabels: Array;
27 | keydownListener: any;
28 | settings: any;
29 | statusBarElement: HTMLElement | null;
30 |
31 | constructor(serializedState: any, stateMachine: any) {
32 | this.workspaceElement = atom.views.getView(atom.workspace);
33 | this.disposables = new CompositeDisposable();
34 | this.drawnLabels = [];
35 | this.commands = new CompositeDisposable();
36 | this.stateMachine = stateMachine;
37 |
38 | this.setSettings();
39 |
40 | // Subscribe:
41 | this.stateMachine.ports.validKeyEntered.subscribe((keyLabel: string) => {
42 | for (const label of this.drawnLabels) {
43 | if (!label.keyLabel || !label.element) {
44 | continue;
45 | }
46 | if (!label.keyLabel.startsWith(keyLabel)) {
47 | label.element.classList.add('irrelevant');
48 | }
49 | }
50 |
51 | this.currentLabels = labelReducer(this.currentLabels, keyLabel);
52 | });
53 |
54 | this.stateMachine.ports.labelJumped.subscribe((keyLabel: string) => {
55 | _.find(this.currentLabels, (label) => label.keyLabel === keyLabel).jump();
56 | });
57 |
58 | this.stateMachine.ports.activeChanged.subscribe((active: boolean) => {
59 | this.active = active;
60 |
61 | if (!this.active) {
62 | this.turnOffListeners();
63 | this.clearJumpMode();
64 | }
65 | });
66 |
67 | this.stateMachine.ports.statusChanged.subscribe((statusMarkup: string) => {
68 | if (this.statusBarElement) {
69 | this.statusBarElement.innerHTML = statusMarkup;
70 | }
71 | });
72 |
73 | this.keydownListener = (event: any) => {
74 | // use the code property for testing if
75 | // the key is relevant to Jumpy
76 | // that is, that it's an alpha char.
77 | // use the key character to pass the exact key
78 | // that is, (upper or lower) to the state machine.
79 | // if jumpy catches it...stop the event propagation.
80 | const {code, key, metaKey, ctrlKey, altKey} = event;
81 | if (metaKey || ctrlKey || altKey) {
82 | return;
83 | }
84 |
85 | if (/^Key[A-Z]{1}$/.test(code)) {
86 | event.preventDefault();
87 | event.stopPropagation();
88 | this.stateMachine.ports.key.send(key.charCodeAt());
89 | }
90 | };
91 |
92 | this.commands.add(atom.commands.add('atom-workspace', {
93 | 'jumpy:toggle': () => { this.toggle() },
94 | 'jumpy:reset': () => { this.reset(); },
95 | 'jumpy:clear': () => {
96 | this.stateMachine.ports.exit.send(null);
97 | }
98 | }));
99 | }
100 |
101 | initializeStatusBar() {
102 | // NOTE: This needs to be called when status bar is ready, so can't be called from constructor
103 |
104 | if (this.statusBarElement) {
105 | return;
106 | }
107 |
108 | const atomStatusBar = document.querySelector('status-bar');
109 | if (atomStatusBar) {
110 | const statusBarElement = document.createElement('div');
111 | this.statusBarElement = statusBarElement;
112 | statusBarElement.id = 'status-bar-jumpy-container';
113 | statusBarElement.classList.add('inline-block');
114 | statusBarElement.innerHTML = "Jumpy: Jump Mode!
";
115 | atomStatusBar.addLeftTile({
116 | item: statusBarElement,
117 | priority: -1
118 | });
119 | }
120 | }
121 |
122 | setSettings() {
123 | let fontSize:number = atom.config.get('jumpy.fontSize');
124 | if (isNaN(fontSize) || fontSize > 1) {
125 | fontSize = .75; // default
126 | }
127 | const fontSizeString:string = `${fontSize * 100}%`;
128 | this.settings = {
129 | fontSize: fontSizeString,
130 | highContrast: atom.config.get('jumpy.highContrast'),
131 | wordsPattern: new RegExp (atom.config.get('jumpy.matchPattern'), 'g')
132 | };
133 | }
134 |
135 | reset() {
136 | this.currentLabels = _.clone(this.allLabels);
137 | for (const label of this.currentLabels) {
138 | if (label.element) {
139 | label.element.classList.remove('irrelevant');
140 | }
141 | }
142 | this.stateMachine.ports.reset.send(null);
143 | }
144 |
145 | loadLabels() {
146 | const environment:LabelEnvironment = {
147 | keys: getKeySet(atom.config.get('jumpy.customKeys')),
148 | settings: this.settings
149 | };
150 |
151 | // TODO: reduce with concat all labelers -> labeler.getLabels()
152 | const wordLabels:Array = getWordLabels(environment);
153 | const tabLabels:Array = getTabLabels(environment);
154 | const treeItemLabels:Array = getTreeItemLabels(environment);
155 |
156 | // TODO: I really think alllabels can just be drawnlabels
157 | // maybe I call labeler.draw() still returns back anyway? Less functional?
158 | this.allLabels = [
159 | ...wordLabels,
160 | ...tabLabels
161 | ...treeItemLabels
162 | ];
163 |
164 | for (const label of this.allLabels) {
165 | this.drawnLabels.push(label.drawLabel());
166 | }
167 |
168 | this.currentLabels = _.clone(this.allLabels);
169 |
170 | this.stateMachine.ports.getLabels.send(
171 | this.currentLabels
172 | .filter((label) => label.keyLabel) // ie. tabs open after limit reached
173 | .map((label) => label.keyLabel)
174 | );
175 | }
176 |
177 | toggle() {
178 | if (!this.active) {
179 | this.loadLabels();
180 | this.initializeStatusBar();
181 | this.turnOnListeners();
182 |
183 | } else { // Turn off:
184 | this.stateMachine.ports.exit.send(null);
185 | }
186 | }
187 |
188 | turnOnListeners() {
189 | this.workspaceElement.addEventListener('keydown', this.keydownListener, true);
190 | for (const e of ['blur', 'click', 'scroll']) {
191 | this.workspaceElement.addEventListener(e, () => this.clearJumpModeHandler(), true);
192 | }
193 | }
194 |
195 | turnOffListeners() {
196 | this.workspaceElement.removeEventListener('keydown', this.keydownListener, true);
197 | for (const e of ['blur', 'click', 'scroll']) {
198 | this.workspaceElement.removeEventListener(e, () => this.clearJumpModeHandler(), true);
199 | }
200 | }
201 |
202 | clearJumpModeHandler() {
203 | this.stateMachine.ports.exit.send(null);
204 | this.clearJumpMode();
205 | }
206 |
207 | clearJumpMode() {
208 | const clearAllLabels = () => {
209 | for (const label of this.drawnLabels) {
210 | label.destroy();
211 | }
212 | this.drawnLabels = []; // Very important for GC.
213 | // Verifiable in Dev Tools -> Timeline -> Nodes.
214 | };
215 |
216 | this.allLabels = [];
217 | const treeView:HTMLCollectionOf = document.getElementsByClassName('tree-view');
218 | if (treeView.length) {
219 | removeJumpModeClasses(treeView[0]);
220 | }
221 | for (const editor of atom.workspace.getTextEditors()) {
222 | const editorView = atom.views.getView(editor);
223 | removeJumpModeClasses(editorView);
224 | }
225 | clearAllLabels();
226 | if (this.disposables) {
227 | this.disposables.dispose();
228 | }
229 | }
230 |
231 | // Returns an object that can be retrieved when package is activated
232 | serialize() {}
233 |
234 | // Tear down any state and detach
235 | destroy() {
236 | if (this.commands) {
237 | this.commands.dispose();
238 | }
239 | this.clearJumpMode();
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/lib/jumpy.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | import * as elmApp from '../dist/elm/StateMachine';
4 | import JumpyView from './jumpy-view';
5 |
6 | module.exports = {
7 |
8 | jumpyView: null,
9 | config: {
10 | fontSize: {
11 | description: 'The font size of jumpy labels.',
12 | type: 'number',
13 | default: .75,
14 | minimum: 0,
15 | maximum: 1
16 | },
17 | highContrast: {
18 | description: 'This will display a high contrast label, \
19 | usually green. It is dynamic per theme.',
20 | type: 'boolean',
21 | default: false
22 | },
23 | useHomingBeaconEffectOnJumps: {
24 | description: 'This will animate a short lived homing beacon upon \
25 | jump.',
26 | type: 'boolean',
27 | default: true
28 | },
29 | matchPattern: {
30 | description: 'Jumpy will create labels based on this pattern.',
31 | type: 'string',
32 | default: '([A-Z]+([0-9a-z])*)|[a-z0-9]{2,}'
33 | },
34 | customKeys: {
35 | description: 'Jumpy will use these characters in the specifed order to create labels (comma separated)',
36 | type: 'array',
37 | default: []
38 | }
39 | },
40 |
41 | activate(state: any) {
42 | const stateMachine = elmApp.Elm.StateMachine.init();
43 | this.jumpyView = new JumpyView(state.jumpyViewState, stateMachine);
44 | },
45 |
46 | deactivate() {
47 | if (this.jumpyView) {
48 | this.jumpyView.destroy();
49 | }
50 | this.jumpyView = null;
51 | },
52 |
53 | serialize() {
54 | return {
55 | jumpyViewState: this.jumpyView.serialize(),
56 | };
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/lib/keys.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | import * as _ from 'lodash';
4 |
5 | export function getKeySet(customKeys: Array) {
6 | let lowerCharacters: Array = [];
7 | let upperCharacters: Array = [];
8 |
9 | if (!customKeys.length) {
10 | lowerCharacters = _.range('a'.charCodeAt(0), 'z'.charCodeAt(0) + 1 /* for inclusive*/)
11 | .map(c => String.fromCharCode(c));
12 | upperCharacters = _.range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1 /* for inclusive*/)
13 | .map(c => String.fromCharCode(c));
14 | } else {
15 | for (let key of customKeys) {
16 | lowerCharacters.push(key.toLowerCase());
17 | upperCharacters.push(key.toUpperCase());
18 | }
19 | }
20 |
21 | const keys: Array = [];
22 |
23 | // A little ugly.
24 | // I used itertools.permutation in python.
25 | // Couldn't find a good one in npm. Don't worry this takes < 1ms once.
26 | // TODO: try a zip? and or make a func
27 | for (let c1 of lowerCharacters) {
28 | for (let c2 of lowerCharacters) {
29 | keys.push(c1 + c2);
30 | }
31 | }
32 | for (let c1 of upperCharacters) {
33 | for (let c2 of lowerCharacters) {
34 | keys.push(c1 + c2);
35 | }
36 | }
37 | for (let c1 of lowerCharacters) {
38 | for (let c2 of upperCharacters) {
39 | keys.push(c1 + c2);
40 | }
41 | }
42 |
43 | // TODO: use TS's ReadonlyArray?
44 | return keys;
45 | }
46 |
--------------------------------------------------------------------------------
/lib/label-interface.ts:
--------------------------------------------------------------------------------
1 | import { TextEditor } from 'atom';
2 |
3 | export interface LabelEnvironment {
4 | keys: Array;
5 | settings: any;
6 | }
7 |
8 | export interface Label {
9 | // TODO: can I make this | null instead of undefined?
10 | keyLabel: string | undefined;
11 | textEditor: TextEditor | null;
12 | element: HTMLElement | null;
13 | settings: any;
14 | drawLabel(): Label;
15 | animateBeacon(input: any): void;
16 | jump(): void;
17 | destroy(): void;
18 | }
19 |
20 | export interface Labeler {
21 | (environment:LabelEnvironment):Array;
22 | }
23 |
--------------------------------------------------------------------------------
/lib/label-reducer.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | import { Label } from './label-interface';
4 |
5 | // (PURE FUNCTION)
6 | //
7 | // WHEN GIVEN:
8 | //
9 | // 1. AN ARRAY OF LABELS (* SEE BELOW)
10 | // (and)
11 | // 2. A NEW INPUT KEY
12 | //
13 | // RETURNS new collection of labels
14 | // *without* the labels that do not start with the current key
15 |
16 | export default function labelReducer (labels: Array, currentKey : string) {
17 | return labels.filter(function(label : Label) {
18 | if (!label.keyLabel) {
19 | return false;
20 | }
21 | return label.keyLabel.startsWith(currentKey);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/lib/labelers/tabs.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | import { LabelEnvironment, Label, Labeler } from '../label-interface';
4 | import { TextEditor, Pane } from 'atom';
5 |
6 | class TabLabel implements Label {
7 | // TODO: check I need these defined again?
8 | keyLabel: string | undefined;
9 | textEditor: TextEditor | null;
10 | element: HTMLElement | null;
11 | settings: any;
12 |
13 | destroy() {
14 | if (this.element) {
15 | this.element.remove();
16 | }
17 | }
18 |
19 | drawLabel(): Label {
20 | const tabsPane:Pane = atom.workspace.paneForItem(this.textEditor);
21 | const tabsPaneElement:HTMLElement = atom.views.getView(tabsPane);
22 | const foundTab:HTMLElement | null = tabsPaneElement
23 | .querySelector(`[data-path='${this.textEditor.getPath()}']`);
24 |
25 | if (!foundTab) {
26 | return this;
27 | }
28 |
29 | const labelElement:HTMLElement = document.createElement('div');
30 | if (this.keyLabel) {
31 | labelElement.textContent = this.keyLabel;
32 | }
33 | labelElement.style.position = 'fixed';
34 | labelElement.classList.add('jumpy-label'); // For styling and tests
35 | labelElement.classList.add('tab-label');
36 | labelElement.style.fontSize = this.settings.fontSize;
37 |
38 | if (this.settings.highContrast) {
39 | labelElement.classList.add('high-contrast');
40 | }
41 |
42 | this.element = labelElement;
43 | foundTab.appendChild(labelElement);
44 |
45 | return this;
46 | }
47 |
48 | animateBeacon() {
49 | // TODO: abstract function to find tab!
50 | const tabsPane:Pane = atom.workspace.paneForItem(this.textEditor);
51 | const tabsPaneElement:HTMLElement = atom.views.getView(tabsPane);
52 | const foundTab:HTMLElement | null = tabsPaneElement
53 | .querySelector(`[data-path='${this.textEditor.getPath()}'`);
54 |
55 | if (foundTab) {
56 | const beacon = document.createElement('span');
57 | beacon.style.position = 'relative';
58 | beacon.style.zIndex = '4';
59 | beacon.classList.add('beacon'); // For styling and tests
60 | beacon.classList.add('tab-beacon');
61 |
62 | foundTab.appendChild(beacon);
63 | setTimeout(function() {
64 | beacon.remove();
65 | } , 150);
66 | }
67 | }
68 |
69 | jump() {
70 | const pane = atom.workspace.paneForItem(this.textEditor);
71 | pane.activate();
72 | pane.activateItem(this.textEditor);
73 |
74 | if (atom.config.get('jumpy.useHomingBeaconEffectOnJumps')) {
75 | this.animateBeacon();
76 | }
77 | }
78 | }
79 |
80 | const labeler: Labeler = function(env:LabelEnvironment):Array {
81 | const labels:Array = [];
82 |
83 | for (const textEditor of atom.workspace.getPaneItems()) {
84 | if (!(textEditor instanceof TextEditor) || !textEditor.buffer) {
85 | continue;
86 | }
87 |
88 | const keyLabel:string | undefined = env.keys.shift();
89 |
90 | const label = new TabLabel();
91 | label.settings = env.settings;
92 | label.keyLabel = keyLabel;
93 | label.textEditor = textEditor;
94 | labels.push(label);
95 | }
96 |
97 | return labels;
98 | }
99 |
100 | export default labeler;
101 |
--------------------------------------------------------------------------------
/lib/labelers/tree-items.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | import { LabelEnvironment, Label, Labeler } from '../label-interface';
4 | import { addJumpModeClasses } from '../viewHelpers';
5 | import { TextEditor, Pane } from 'atom';
6 |
7 | class TreeItemLabel implements Label {
8 | // TODO: check I need these defined again?
9 | keyLabel: string | undefined;
10 | textEditor: TextEditor | null;
11 | element: HTMLElement | null;
12 | settings: any;
13 |
14 | // TreeItemLabel specific:
15 | item: HTMLElement;
16 |
17 | destroy() {
18 | if (this.element) {
19 | this.element.remove();
20 | }
21 | }
22 |
23 | drawLabel(): Label {
24 | const labelElement:HTMLElement = document.createElement('div');
25 | if (this.keyLabel) {
26 | labelElement.textContent = this.keyLabel;
27 | }
28 | labelElement.style.position = 'absolute';
29 | labelElement.classList.add('jumpy-label'); // For styling and tests
30 | labelElement.classList.add('tree-item-label');
31 | labelElement.style.fontSize = this.settings.fontSize;
32 |
33 | if (this.settings.highContrast) {
34 | labelElement.classList.add('high-contrast');
35 | }
36 |
37 | this.element = labelElement;
38 | const parent = this.item.parentElement
39 | if (parent) {
40 | parent.appendChild(labelElement);
41 | }
42 |
43 | return this;
44 | }
45 |
46 | animateBeacon() {
47 | const beacon = document.createElement('span');
48 | beacon.style.position = 'relative';
49 | beacon.style.zIndex = '4';
50 | beacon.classList.add('beacon'); // For styling and tests
51 | beacon.classList.add('tree-item-beacon');
52 |
53 | this.item.appendChild(beacon);
54 | setTimeout(function() {
55 | beacon.remove();
56 | } , 150);
57 | }
58 |
59 | jump() {
60 | function triggerMouseEvent(element:HTMLElement, eventType:string): void {
61 | var clickEvent = new MouseEvent("click", {
62 | bubbles: true,
63 | cancelable: true,
64 | view: window
65 | });
66 | element.dispatchEvent(clickEvent);
67 | }
68 | const treeItem = this.item.parentElement;
69 | if (treeItem) {
70 | // TODO: use only 1 of these with a guard statement.
71 | atom.commands.dispatch(treeItem, 'tree-view:toggle-focus');
72 | triggerMouseEvent(treeItem, 'mousedown');
73 | atom.commands.dispatch(treeItem, 'tree-view:toggle-focus');
74 | }
75 | // this.item.parentElement.click();
76 | if (atom.config.get('jumpy.useHomingBeaconEffectOnJumps')) {
77 | this.animateBeacon();
78 | }
79 | }
80 | }
81 |
82 | const labeler: Labeler = function(env:LabelEnvironment):Array {
83 | const labels:Array = [];
84 |
85 | // just here for hotkey cascading:
86 | const treeView = document.querySelector('.tree-view');
87 | if (treeView) {
88 | addJumpModeClasses(treeView);
89 | }
90 |
91 | const treeItems:NodeListOf = document.querySelectorAll('.tree-view-root .directory .list-item .name');
92 |
93 | for (const treeItem of treeItems) {
94 | const keyLabel:string | undefined = env.keys.shift();
95 |
96 | const label = new TreeItemLabel();
97 | label.settings = env.settings;
98 | label.keyLabel = keyLabel;
99 | label.item = treeItem;
100 | labels.push(label);
101 | }
102 |
103 | return labels;
104 | }
105 |
106 | export default labeler;
107 |
--------------------------------------------------------------------------------
/lib/labelers/words.ts:
--------------------------------------------------------------------------------
1 | 'use babel';
2 |
3 | import * as _ from 'lodash';
4 | import { LabelEnvironment, Label, Labeler } from '../label-interface';
5 | import { addJumpModeClasses } from '../viewHelpers';
6 | import { Point, Range, TextEditor } from 'atom';
7 |
8 | function getVisibleColumnRange (editorView: any): Array {
9 | const charWidth = editorView.getDefaultCharacterWidth()
10 | // FYI: asserts:
11 | // numberOfVisibleColumns = editorView.getWidth() / charWidth
12 | const minColumn = (editorView.getScrollLeft() / charWidth) - 1
13 | const maxColumn = editorView.getScrollRight() / charWidth
14 |
15 | return [
16 | minColumn,
17 | maxColumn
18 | ];
19 | }
20 |
21 | // Taken from jQuery: https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
22 | function isVisible(element) {
23 | return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
24 | }
25 |
26 | class WordLabel implements Label {
27 | // TODO: check I need these defined again?
28 | keyLabel: string | undefined;
29 | textEditor: TextEditor | null;
30 | element: HTMLElement | null;
31 | settings: any;
32 |
33 | // WordLabel specific:
34 | lineNumber: number;
35 | column: number;
36 | marker: any;
37 |
38 | destroy() {
39 | this.marker.destroy();
40 | }
41 |
42 | drawLabel(): Label {
43 | const { textEditor, lineNumber, column, keyLabel } = this;
44 |
45 | this.marker = textEditor.markScreenRange(new Range(
46 | new Point(lineNumber, column),
47 | new Point(lineNumber, column)),
48 | { invalidate: 'touch'});
49 |
50 | const labelElement = document.createElement('div');
51 | labelElement.textContent = keyLabel;
52 | labelElement.style.fontSize = this.settings.fontSize;
53 | labelElement.classList.add('jumpy-label'); // For styling and tests
54 |
55 | if (this.settings.highContrast) {
56 | labelElement.classList.add('high-contrast');
57 | }
58 |
59 | const decoration = textEditor.decorateMarker(this.marker,
60 | {
61 | type: 'overlay',
62 | item: labelElement,
63 | position: 'head'
64 | });
65 | this.element = labelElement;
66 | return this;
67 | }
68 |
69 | animateBeacon(input: any) {
70 | const position = input;
71 | const range = Range(position, position);
72 | const marker = this.textEditor.markScreenRange(range, { invalidate: 'never' });
73 | const beacon = document.createElement('span');
74 | beacon.classList.add('beacon'); // For styling and tests
75 | this.textEditor.decorateMarker(marker,
76 | {
77 | item: beacon,
78 | type: 'overlay'
79 | });
80 | setTimeout(function() {
81 | marker.destroy();
82 | } , 150);
83 | }
84 |
85 | jump() {
86 | const currentEditor = this.textEditor;
87 | const editorView = atom.views.getView(currentEditor);
88 |
89 | // TODO: pretty sure this can't be useful...anymore
90 | // I think it had somethign to do with the observers etc.
91 | // Prevent other editors from jumping cursors as well
92 | // TODO: make a test for this if return
93 | if (currentEditor.id !== this.textEditor.id) {
94 | return;
95 | }
96 |
97 | const pane = atom.workspace.paneForItem(currentEditor);
98 | pane.activate();
99 |
100 | // isVisualMode is for vim-mode or vim-mode-plus:
101 | const isVisualMode = editorView.classList.contains('visual-mode');
102 | // isSelected is for regular selection in atom or in insert-mode in vim
103 | const isSelected = (currentEditor.getSelections().length === 1 &&
104 | currentEditor.getSelectedText() !== '');
105 | const position = Point(this.lineNumber, this.column);
106 | if (isVisualMode || isSelected) {
107 | currentEditor.selectToScreenPosition(position);
108 | } else {
109 | currentEditor.setCursorScreenPosition(position);
110 | }
111 |
112 | if (atom.config.get('jumpy.useHomingBeaconEffectOnJumps')) {
113 | this.animateBeacon(position);
114 | }
115 | }
116 | }
117 |
118 | const labeler: Labeler = function(env:LabelEnvironment):Array {
119 | const labels:Array = [];
120 | env.settings.wordsPattern.lastIndex = 0; // reset the RegExp for subsequent calls.
121 | for (const textEditor:AtomCore.IEditor of >atom.workspace.getTextEditors()) {
122 | const editorView = atom.views.getView(textEditor);
123 |
124 | // 'jumpy-jump-mode is for keymaps and utilized by tests
125 | addJumpModeClasses(editorView);
126 |
127 | // current labels for current textEditor in loop.
128 | if (!env.keys.length) {
129 | continue;
130 | }
131 |
132 | const [ minColumn, maxColumn ] = getVisibleColumnRange(editorView);
133 | const rows = textEditor.getVisibleRowRange();
134 |
135 | if (!rows || !isVisible(editorView)) {
136 | continue;
137 | }
138 |
139 | const [ firstVisibleRow, lastVisibleRow ] = rows;
140 | // TODO: Right now there are issues with lastVisbleRow
141 | for (const lineNumber of _.range(firstVisibleRow, lastVisibleRow) /*excludes end value*/) {
142 | const lineContents = textEditor.lineTextForScreenRow(lineNumber);
143 | if (textEditor.isFoldedAtScreenRow(lineNumber)) {
144 | if (!env.keys.length) {
145 | continue; // try continue?
146 | }
147 |
148 | const keyLabel = env.keys.shift();
149 |
150 | const label = new WordLabel();
151 | label.settings = env.settings;
152 | label.textEditor = textEditor;
153 | label.keyLabel = keyLabel;
154 | label.lineNumber = lineNumber;
155 | label.column = 0;
156 | labels.push(label);
157 | } else {
158 | let word: any;
159 | while ((word = env.settings.wordsPattern.exec(lineContents)) != null && env.keys.length) {
160 | const keyLabel = env.keys.shift()
161 |
162 | const column = word.index;
163 | // Do not do anything... markers etc.
164 | // if the columns are out of bounds...
165 | if (column > minColumn && column < maxColumn) {
166 | const label = new WordLabel();
167 | label.settings = env.settings;
168 | label.textEditor = textEditor;
169 | label.keyLabel = keyLabel;
170 | label.lineNumber = lineNumber;
171 | label.column = column;
172 | labels.push(label);
173 | }
174 | }
175 | }
176 | } // end: each line
177 | } // end: for each textEditor
178 |
179 | return labels;
180 | }
181 |
182 | export default labeler;
183 |
--------------------------------------------------------------------------------
/lib/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "forceConsistentCasingInFileNames": true,
4 | "importHelpers": true,
5 | "lib": ["es6", "dom"],
6 | "module": "commonjs",
7 | "noLib": false,
8 | "outDir": "../dist",
9 | "preserveConstEnums": true,
10 | "skipLibCheck": true,
11 | "sourceMap": true,
12 | "strict": true,
13 | "target": "es6"
14 | },
15 | "compileOnSave": true
16 | }
17 |
--------------------------------------------------------------------------------
/lib/viewHelpers.ts:
--------------------------------------------------------------------------------
1 | export function addJumpModeClasses(element: HTMLElement) {
2 | element.classList.add('jumpy-jump-mode',
3 | 'jumpy-more-specific1', 'jumpy-more-specific2');
4 | }
5 |
6 | export function removeJumpModeClasses(element: HTMLElement) {
7 | element.classList.remove('jumpy-jump-mode',
8 | 'jumpy-more-specific1', 'jumpy-more-specific2');
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jumpy",
3 | "author": "David L. Goldberg",
4 | "atomTestRunner": "./spec/custom-runner",
5 | "main": "./dist/jumpy",
6 | "version": "5.0.2",
7 | "description": "An Atom package that creates dynamic hotkeys to jump around files across visible panes.",
8 | "keywords": [
9 | "hotkey",
10 | "hotkeys",
11 | "key bindings",
12 | "cursor",
13 | "jump",
14 | "panes",
15 | "shortcuts",
16 | "navigation",
17 | "productivity",
18 | "mouseless",
19 | "plugin",
20 | "extension",
21 | "jumpy"
22 | ],
23 | "repository": "https://github.com/DavidLGoldberg/jumpy",
24 | "license": "MIT",
25 | "engines": {
26 | "atom": ">0.50.0"
27 | },
28 | "dependencies": {
29 | "lodash": "4.17.10",
30 | "space-pen": "^4.2.2"
31 | },
32 | "devDependencies": {
33 | "@types/atom": "0.0.38",
34 | "@types/lodash": "4.14.77",
35 | "atom-jasmine3-test-runner": "3.1.0",
36 | "elm-test": "0.19.0-beta8",
37 | "madge": "3.2.0",
38 | "typescript": "2.5.3",
39 | "uglify-js": "3.4.9"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/spec/custom-runner.js:
--------------------------------------------------------------------------------
1 | const { createRunner } = require('atom-jasmine3-test-runner');
2 |
3 | // optional options to customize the runner
4 | const extraOptions = {
5 | suffix: "-spec",
6 | specHelper: {
7 | atom: false,
8 | attachToDom: true,
9 | ci: true,
10 | customMatchers: true,
11 | jasmineFocused: false,
12 | jasmineJson: false,
13 | jasminePass: false,
14 | jasmineShouldFail: false,
15 | jasmineTagged: false,
16 | mockClock: false, // this was key for setTimeout.
17 | mockLocalStorage: false,
18 | pathwatcher: true, // finds leaking subscriptions after each test.
19 | profile: false, // might want to use this to profile in the future!
20 | set: false,
21 | unspy: false
22 | }
23 | };
24 |
25 | module.exports = createRunner(extraOptions);
26 |
--------------------------------------------------------------------------------
/spec/fixtures/test_long_text.md:
--------------------------------------------------------------------------------
1 | aa ab ac ad ae af ag ah ai aj ak al am an ao ap aq ar as at au av aw ax ay az
2 | ba bb bc bd be bf bg bh bi bj bk bl bm bn bo bp bq br bs bt bu bv bw bx by bz
3 | ca cb cc cd ce cf cg ch ci cj ck cl cm cn co cp cq cr cs ct cu cv cw cx cy cz
4 | da db dc dd de df dg dh di dj dk dl dm dn do dp dq dr ds dt du dv dw dx dy dz
5 | ea eb ec ed ee ef eg eh ei ej ek el em en eo ep eq er es et eu ev ew ex ey ez
6 | fa fb fc fd fe ff fg fh fi fj fk fl fm fn fo fp fq fr fs ft fu fv fw fx fy fz
7 | ga gb gc gd ge gf gg gh gi gj gk gl gm gn go gp gq gr gs gt gu gv gw gx gy gz
8 | ha hb hc hd he hf hg hh hi hj hk hl hm hn ho hp hq hr hs ht hu hv hw hx hy hz
9 | ia ib ic id ie if ig ih ii ij ik il im in io ip iq ir is it iu iv iw ix iy iz
10 | ja jb jc jd je jf jg jh ji jj jk jl jm jn jo jp jq jr js jt ju jv jw jx jy jz
11 | ka kb kc kd ke kf kg kh ki kj kk kl km kn ko kp kq kr ks kt ku kv kw kx ky kz
12 | la lb lc ld le lf lg lh li lj lk ll lm ln lo lp lq lr ls lt lu lv lw lx ly lz
13 | ma mb mc md me mf mg mh mi mj mk ml mm mn mo mp mq mr ms mt mu mv mw mx my mz
14 | na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt nu nv nw nx ny nz
15 | oa ob oc od oe of og oh oi oj ok ol om on oo op oq or os ot ou ov ow ox oy oz
16 | pa pb pc pd pe pf pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw px py pz
17 | qa qb qc qd qe qf qg qh qi qj qk ql qm qn qo qp qq qr qs qt qu qv qw qx qy qz
18 | ra rb rc rd re rf rg rh ri rj rk rl rm rn ro rp rq rr rs rt ru rv rw rx ry rz
19 | sa sb sc sd se sf sg sh si sj sk sl sm sn so sp sq sr ss st su sv sw sx sy sz
20 | ta tb tc td te tf tg th ti tj tk tl tm tn to tp tq tr ts tt tu tv tw tx ty tz
21 | ua ub uc ud ue uf ug uh ui uj uk ul um un uo up uq ur us ut uu uv uw ux uy uz
22 | va vb vc vd ve vf vg vh vi vj vk vl vm vn vo vp vq vr vs vt vu vv vw vx vy vz
23 | wa wb wc wd we wf wg wh wi wj wk wl wm wn wo wp wq wr ws wt wu wv ww wx wy wz
24 | xa xb xc xd xe xf xg xh xi xj xk xl xm xn xo xp xq xr xs xt xu xv xw xx xy xz
25 | ya yb yc yd ye yf yg yh yi yj yk yl ym yn yo yp yq yr ys yt yu yv yw yx yy yz
26 | za zb zc zd ze zf zg zh zi zj zk zl zm zn zo zp zq zr zs zt zu zv zw zx zy zz
27 | Aa Ab Ac Ad Ae Af Ag Ah Ai Aj Ak Al Am An Ao Ap Aq Ar As At Au Av Aw Ax Ay Az
28 | Ba Bb Bc Bd Be Bf Bg Bh Bi Bj Bk Bl Bm Bn Bo Bp Bq Br Bs Bt Bu Bv Bw Bx By Bz
29 | Ca Cb Cc Cd Ce Cf Cg Ch Ci Cj Ck Cl Cm Cn Co Cp Cq Cr Cs Ct Cu Cv Cw Cx Cy Cz
30 | Da Db Dc Dd De Df Dg Dh Di Dj Dk Dl Dm Dn Do Dp Dq Dr Ds Dt Du Dv Dw Dx Dy Dz
31 | Ea Eb Ec Ed Ee Ef Eg Eh Ei Ej Ek El Em En Eo Ep Eq Er Es Et Eu Ev Ew Ex Ey Ez
32 | Fa Fb Fc Fd Fe Ff Fg Fh Fi Fj Fk Fl Fm Fn Fo Fp Fq Fr Fs Ft Fu Fv Fw Fx Fy Fz
33 | Ga Gb Gc Gd Ge Gf Gg Gh Gi Gj Gk Gl Gm Gn Go Gp Gq Gr Gs Gt Gu Gv Gw Gx Gy Gz
34 | Ha Hb Hc Hd He Hf Hg Hh Hi Hj Hk Hl Hm Hn Ho Hp Hq Hr Hs Ht Hu Hv Hw Hx Hy Hz
35 | Ia Ib Ic Id Ie If Ig Ih Ii Ij Ik Il Im In Io Ip Iq Ir Is It Iu Iv Iw Ix Iy Iz
36 | Ja Jb Jc Jd Je Jf Jg Jh Ji Jj Jk Jl Jm Jn Jo Jp Jq Jr Js Jt Ju Jv Jw Jx Jy Jz
37 | Ka Kb Kc Kd Ke Kf Kg Kh Ki Kj Kk Kl Km Kn Ko Kp Kq Kr Ks Kt Ku Kv Kw Kx Ky Kz
38 | La Lb Lc Ld Le Lf Lg Lh Li Lj Lk Ll Lm Ln Lo Lp Lq Lr Ls Lt Lu Lv Lw Lx Ly Lz
39 | Ma Mb Mc Md Me Mf Mg Mh Mi Mj Mk Ml Mm Mn Mo Mp Mq Mr Ms Mt Mu Mv Mw Mx My Mz
40 | Na Nb Nc Nd Ne Nf Ng Nh Ni Nj Nk Nl Nm Nn No Np Nq Nr Ns Nt Nu Nv Nw Nx Ny Nz
41 | Oa Ob Oc Od Oe Of Og Oh Oi Oj Ok Ol Om On Oo Op Oq Or Os Ot Ou Ov Ow Ox Oy Oz
42 | Pa Pb Pc Pd Pe Pf Pg Ph Pi Pj Pk Pl Pm Pn Po Pp Pq Pr Ps Pt Pu Pv Pw Px Py Pz
43 | Qa Qb Qc Qd Qe Qf Qg Qh Qi Qj Qk Ql Qm Qn Qo Qp Qq Qr Qs Qt Qu Qv Qw Qx Qy Qz
44 | Ra Rb Rc Rd Re Rf Rg Rh Ri Rj Rk Rl Rm Rn Ro Rp Rq Rr Rs Rt Ru Rv Rw Rx Ry Rz
45 | Sa Sb Sc Sd Se Sf Sg Sh Si Sj Sk Sl Sm Sn So Sp Sq Sr Ss St Su Sv Sw Sx Sy Sz
46 | Ta Tb Tc Td Te Tf Tg Th Ti Tj Tk Tl Tm Tn To Tp Tq Tr Ts Tt Tu Tv Tw Tx Ty Tz
47 | Ua Ub Uc Ud Ue Uf Ug Uh Ui Uj Uk Ul Um Un Uo Up Uq Ur Us Ut Uu Uv Uw Ux Uy Uz
48 | Va Vb Vc Vd Ve Vf Vg Vh Vi Vj Vk Vl Vm Vn Vo Vp Vq Vr Vs Vt Vu Vv Vw Vx Vy Vz
49 | Wa Wb Wc Wd We Wf Wg Wh Wi Wj Wk Wl Wm Wn Wo Wp Wq Wr Ws Wt Wu Wv Ww Wx Wy Wz
50 | Xa Xb Xc Xd Xe Xf Xg Xh Xi Xj Xk Xl Xm Xn Xo Xp Xq Xr Xs Xt Xu Xv Xw Xx Xy Xz
51 | Ya Yb Yc Yd Ye Yf Yg Yh Yi Yj Yk Yl Ym Yn Yo Yp Yq Yr Ys Yt Yu Yv Yw Yx Yy Yz
52 | Za Zb Zc Zd Ze Zf Zg Zh Zi Zj Zk Zl Zm Zn Zo Zp Zq Zr Zs Zt Zu Zv Zw Zx Zy Zz
53 | aA aB aC aD aE aF aG aH aI aJ aK aL aM aN aO aP aQ aR aS aT aU aV aW aX aY aZ
54 | bA bB bC bD bE bF bG bH bI bJ bK bL bM bN bO bP bQ bR bS bT bU bV bW bX bY bZ
55 | cA cB cC cD cE cF cG cH cI cJ cK cL cM cN cO cP cQ cR cS cT cU cV cW cX cY cZ
56 | dA dB dC dD dE dF dG dH dI dJ dK dL dM dN dO dP dQ dR dS dT dU dV dW dX dY dZ
57 | eA eB eC eD eE eF eG eH eI eJ eK eL eM eN eO eP eQ eR eS eT eU eV eW eX eY eZ
58 | fA fB fC fD fE fF fG fH fI fJ fK fL fM fN fO fP fQ fR fS fT fU fV fW fX fY fZ
59 | gA gB gC gD gE gF gG gH gI gJ gK gL gM gN gO gP gQ gR gS gT gU gV gW gX gY gZ
60 | hA hB hC hD hE hF hG hH hI hJ hK hL hM hN hO hP hQ hR hS hT hU hV hW hX hY hZ
61 | iA iB iC iD iE iF iG iH iI iJ iK iL iM iN iO iP iQ iR iS iT iU iV iW iX iY iZ
62 | jA jB jC jD jE jF jG jH jI jJ jK jL jM jN jO jP jQ jR jS jT jU jV jW jX jY jZ
63 | kA kB kC kD kE kF kG kH kI kJ kK kL kM kN kO kP kQ kR kS kT kU kV kW kX kY kZ
64 | lA lB lC lD lE lF lG lH lI lJ lK lL lM lN lO lP lQ lR lS lT lU lV lW lX lY lZ
65 | mA mB mC mD mE mF mG mH mI mJ mK mL mM mN mO mP mQ mR mS mT mU mV mW mX mY mZ
66 | nA nB nC nD nE nF nG nH nI nJ nK nL nM nN nO nP nQ nR nS nT nU nV nW nX nY nZ
67 | oA oB oC oD oE oF oG oH oI oJ oK oL oM oN oO oP oQ oR oS oT oU oV oW oX oY oZ
68 | pA pB pC pD pE pF pG pH pI pJ pK pL pM pN pO pP pQ pR pS pT pU pV pW pX pY pZ
69 | qA qB qC qD qE qF qG qH qI qJ qK qL qM qN qO qP qQ qR qS qT qU qV qW qX qY qZ
70 | rA rB rC rD rE rF rG rH rI rJ rK rL rM rN rO rP rQ rR rS rT rU rV rW rX rY rZ
71 | sA sB sC sD sE sF sG sH sI sJ sK sL sM sN sO sP sQ sR sS sT sU sV sW sX sY sZ
72 | tA tB tC tD tE tF tG tH tI tJ tK tL tM tN tO tP tQ tR tS tT tU tV tW tX tY tZ
73 | uA uB uC uD uE uF uG uH uI uJ uK uL uM uN uO uP uQ uR uS uT uU uV uW uX uY uZ
74 | vA vB vC vD vE vF vG vH vI vJ vK vL vM vN vO vP vQ vR vS vT vU vV vW vX vY vZ
75 | wA wB wC wD wE wF wG wH wI wJ wK wL wM wN wO wP wQ wR wS wT wU wV wW wX wY wZ
76 | xA xB xC xD xE xF xG xH xI xJ xK xL xM xN xO xP xQ xR xS xT xU xV xW xX xY xZ
77 | yA yB yC yD yE yF yG yH yI yJ yK yL yM yN yO yP yQ yR yS yT yU yV yW yX yY yZ
78 | zA zB zC zD zE zF zG zH zI zJ zK zL zM zN zO zP zQ zR zS zT zU zV zW zX zY zZ
79 | no more
80 |
--------------------------------------------------------------------------------
/spec/fixtures/test_text.md:
--------------------------------------------------------------------------------
1 | aa ab ac ad ae
2 | af ag ah ai aj
3 | ak al am an ao
4 | ap aq ar as at
5 | au av aw ax ay az
6 |
7 | ba bb bc bd be
8 | bf bg bh bi bj
9 | bk bl bm bn bo
10 | bp bq br bs bt
11 | bu bv bw bx by bz
12 |
13 | ca cb cc cd ce
14 | cf cg ch ci cj
15 | ck cl cm cn co
16 | cp cq cr cs ct
17 | cu cv cw cx cy cz
18 |
19 | this is a test
20 | this is a test
21 |
22 | * Some
23 | * Foldable stuff
24 | * More foldable stuff (19 Total)
25 | * B1 B1x B1xx
26 | * B2 B2x B2xx
27 | * C1 C1x
28 | * D1 D1x
29 | * E1
30 |
31 | hereIsSomeCamelCase
32 | hereIsSomeMoreCamelCase
33 | here_is_some_underscores
34 |
--------------------------------------------------------------------------------
/spec/fixtures/test_text2.md:
--------------------------------------------------------------------------------
1 | aa ab ac ad ae
2 | af ag ah ai aj
3 | ak al am an ao
4 | ap aq ar as at
5 | au av aw ax ay az
6 |
7 | ba bb bc bd be
8 | bf bg bh bi bj
9 | bk bl bm bn bo
10 | bp bq br bs bt
11 | bu bv bw bx by bz
12 |
13 | ca cb cc cd ce
14 | cf cg ch ci cj
15 | ck cl cm cn co
16 | cp cq cr cs ct
17 | cu cv cw cx cy cz
18 |
19 | this is a test
20 | this is a test
21 |
22 | * Some
23 | * Foldable stuff
24 | * More foldable stuff (19 Total)
25 | * B1 B1x B1xx
26 | * B2 B2x B2xx
27 | * C1 C1x
28 | * D1 D1x
29 | * E1
30 |
31 | hereIsSomeCamelCase
32 | hereIsSomeMoreCamelCase
33 | here_is_some_underscores
34 |
35 | three more words
36 |
--------------------------------------------------------------------------------
/spec/fixtures/test_text_single_letter.md:
--------------------------------------------------------------------------------
1 | aa ab ac ad ae
2 | af ag ah ai aj
3 | ak al am an ao
4 | ap aq ar as at
5 | au av aw ax ay az
6 |
7 | ba
8 |
--------------------------------------------------------------------------------
/spec/helpers/keydown.coffee:
--------------------------------------------------------------------------------
1 | keydown = (key, element) ->
2 | element = element || document.activeElement
3 | event = new KeyboardEvent 'keydown',
4 | code: "Key#{key.toUpperCase()}"
5 | key: key
6 | element.dispatchEvent event
7 |
8 | module.exports = {keydown}
9 |
--------------------------------------------------------------------------------
/spec/helpers/wait.coffee:
--------------------------------------------------------------------------------
1 | wait = (doneFunc, waitTime) ->
2 | setTimeout ->
3 | doneFunc()
4 | , waitTime || 600
5 |
6 | module.exports = {wait}
7 |
--------------------------------------------------------------------------------
/spec/jumpy-large-file-spec.coffee:
--------------------------------------------------------------------------------
1 | ### global
2 | atom
3 | jasmine describe xdescribe beforeEach afterEach it expect
4 | ###
5 | path = require 'path'
6 | {wait} = require './helpers/wait'
7 |
8 | NUM_TOTAL_WORDS = 676 + 676 + 676 + 2 # 2 extra
9 |
10 | describe "Jumpy", ->
11 | [textEditor, textEditorElement] = []
12 |
13 | beforeEach ->
14 | atom.packages.activatePackage 'jumpy'
15 |
16 | beforeEach ->
17 | atom.workspace.open 'test_long_text.md'
18 |
19 | beforeEach ->
20 | # TODO: Abstract the following out, (DRY) --------------
21 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000
22 | atom.project.setPaths([path.join(__dirname, 'fixtures')])
23 | workspaceElement = atom.views.getView(atom.workspace)
24 | # @leedohm helped me with this idiom of workspace size.
25 | # He found it in the wrap-guide.
26 | workspaceElement.style.height = "5000px" # big enough
27 | workspaceElement.style.width = "5000px"
28 | jasmine.attachToDOM(workspaceElement)
29 | # TODO: Abstract the following out, (DRY) --------------
30 |
31 | textEditor = atom.workspace.getActiveTextEditor()
32 | textEditorElement = atom.views.getView(textEditor)
33 | # TODO: Need this like the others?
34 | # textEditor.setCursorBufferPosition [1,1]
35 |
36 | beforeEach (done) ->
37 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
38 | wait(done)
39 |
40 | afterEach ->
41 | expect(atom.workspace.getActivePaneItem().isModified()).toBeFalsy()
42 | atom.workspace.destroy 'test_long_text.md'
43 |
44 | # TODO: Recent patch has slowed down execution of the tests when
45 | # jasmine.attachToDOM is called. Even with decoration (performance
46 | # improvements) this file ('test_long_text.MD') is too large to be loaded!
47 | # It works non jasmine of course...
48 | describe "when jumpy:toggle event is triggered on a large file", ->
49 | it "prints the right labels beyond zz", ->
50 | decorations = textEditor.getOverlayDecorations()
51 | expect(decorations[0].getProperties().item.textContent).toBe 'aa'
52 | expect(decorations[1].getProperties().item.textContent).toBe 'ab'
53 | expect(decorations[676].getProperties().item.textContent).toBe 'Aa'
54 | expect(decorations[677].getProperties().item.textContent).toBe 'Ab'
55 | expect(decorations[676+676]
56 | .getProperties().item.textContent).toBe 'aA'
57 | expect(decorations[676+676+1]
58 | .getProperties().item.textContent).toBe 'aB'
59 | it "does not print undefined labels beyond zA", ->
60 | decorations = textEditor.getOverlayDecorations()
61 | expect(decorations).toHaveLength NUM_TOTAL_WORDS - 2
62 |
--------------------------------------------------------------------------------
/spec/jumpy-multi-pane-spec.coffee:
--------------------------------------------------------------------------------
1 | ### global
2 | atom
3 | jasmine describe xdescribe beforeEach it runs waitsForPromise
4 | ###
5 | path = require 'path'
6 |
7 | NUM_ALPHA_TEST_WORDS = 26 * 3
8 | NUM_ENGLISH_TEXT = 8 - 2 #For a's that are only 1 character. *'s don't count.
9 | NUM_COLLAPSIBLE_WORDS = 19
10 | NUM_CAMEL_WORDS = 3
11 | NUM_TOTAL_WORDS =
12 | NUM_ALPHA_TEST_WORDS +
13 | NUM_ENGLISH_TEXT +
14 | NUM_COLLAPSIBLE_WORDS +
15 | NUM_CAMEL_WORDS
16 |
17 | NUM_CAMEL_SPECIFIC_MATCHES = 4 + 5 + 3
18 |
19 | # TODO: Upgrade to Jasmine 3 and new arch's
20 | xdescribe "Jumpy", ->
21 | [workspaceElement, textEditorElement, textEditor, jumpyPromise] = []
22 |
23 | beforeEach ->
24 | atom.project.setPaths([path.join(__dirname, 'fixtures')])
25 | # TODO: Abstract the following out, (DRY) --------------
26 | workspaceElement = atom.views.getView(atom.workspace)
27 | # @leedohm helped me with this idiom of workspace size.
28 | # He found it in the wrap-guide.
29 | workspaceElement.style.height = "5000px" # big enough
30 | workspaceElement.style.width = "5000px"
31 | jasmine.attachToDOM(workspaceElement)
32 | # TODO: Abstract the following out, (DRY) --------------
33 |
34 | waitsForPromise ->
35 | atom.workspace.open 'test_text.MD'
36 | atom.workspace.open 'test_text.MD'
37 |
38 | runs ->
39 | textEditor = atom.workspace.getActiveTextEditor()
40 | textEditorElement = atom.views.getView(textEditor)
41 | jumpyPromise = atom.packages.activatePackage 'jumpy'
42 | textEditor.setCursorBufferPosition [1,1]
43 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
44 | # TODO: SPLIT PANE (MOVE TO RIGHT)!
45 |
46 | waitsForPromise ->
47 | jumpyPromise
48 |
49 | afterEach ->
50 | expect(atom.workspace.getActivePaneItem().isModified()).toBeFalsy()
51 |
52 | # TODO: IMPLEMENT THIS. Line 192 `pane.activate()` in jumpy-view.coffee
53 | # should be enough to make this red to green.
54 | describe "when jumpy jumps to another pane", ->
55 | it "focuses the new pane", ->
56 | it "does not move cursor of original pane", ->
57 | it "does not make edits (with the entered keys)", ->
58 |
--------------------------------------------------------------------------------
/spec/jumpy-settings-spec.coffee:
--------------------------------------------------------------------------------
1 | ### global
2 | atom
3 | jasmine describe beforeEach it xit runs expect waitsForPromise
4 | ###
5 | path = require 'path'
6 | {keydown} = require './helpers/keydown'
7 | {wait} = require './helpers/wait'
8 |
9 | NUM_ALPHA_TEST_WORDS = 26 * 3
10 | NUM_ENGLISH_TEXT = 8 - 2 #For a's that are only 1 character. *'s don't count.
11 | NUM_COLLAPSIBLE_WORDS = 19
12 | NUM_CAMEL_WORDS = 3
13 | NUM_TOTAL_WORDS =
14 | NUM_ALPHA_TEST_WORDS +
15 | NUM_ENGLISH_TEXT +
16 | NUM_COLLAPSIBLE_WORDS +
17 | NUM_CAMEL_WORDS
18 |
19 | NUM_CAMEL_SPECIFIC_MATCHES = 4 + 5 + 3
20 |
21 | describe "Jumpy with non default settings on", ->
22 | [textEditor, textEditorElement] = []
23 |
24 | beforeEach ->
25 | atom.packages.activatePackage 'jumpy'
26 |
27 | beforeEach ->
28 | atom.workspace.open 'test_text.md'
29 |
30 | beforeEach ->
31 | # TODO: Abstract the following out, (DRY) --------------
32 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000
33 | atom.project.setPaths([path.join(__dirname, 'fixtures')])
34 | workspaceElement = atom.views.getView(atom.workspace)
35 | # @leedohm helped me with this idiom of workspace size.
36 | # He found it in the wrap-guide.
37 | workspaceElement.style.height = "5000px" # big enough
38 | workspaceElement.style.width = "5000px"
39 | jasmine.attachToDOM(workspaceElement)
40 | # TODO: Abstract the following out, (DRY) --------------
41 |
42 | beforeEach (done) ->
43 | atom.config.set 'jumpy.highContrast', true
44 | atom.config.set 'jumpy.fontSize', .50
45 | atom.config.set 'jumpy.useHomingBeaconEffectOnJumps', false
46 | atom.config.set 'jumpy.matchPattern', '([\\w]){2,}' # old Jumpy default
47 | wait(done)
48 |
49 | beforeEach (done) ->
50 | textEditor = atom.workspace.getActiveTextEditor()
51 | textEditorElement = atom.views.getView(textEditor)
52 | textEditor.setCursorBufferPosition [1,1]
53 | wait(done)
54 |
55 | beforeEach (done) ->
56 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
57 | wait(done)
58 |
59 | afterEach ->
60 | expect(atom.workspace.getActivePaneItem().isModified()).toBeFalsy()
61 | atom.workspace.destroy 'test_text.md'
62 |
63 | # TODO: This needs to be fixed ...probably a jasmine 3 thing
64 | xdescribe "when the jumpy:toggle event is triggered", ->
65 | it "draws correctly colored labels", ->
66 | expect(textEditor.getOverlayDecorations()[0].getProperties().item
67 | .classList.contains 'high-contrast').toBe true
68 | it "draws labels of the right font size", ->
69 | expect(textEditor.getOverlayDecorations()[0].getProperties().item
70 | .style.fontSize).toBe '50%'
71 |
72 | describe "when the jumpy:toggle event is triggered
73 | and a jump is performed", ->
74 | beforeEach (done) ->
75 | keydown('a')
76 | wait(done)
77 | beforeEach (done) ->
78 | keydown('c')
79 | wait(done)
80 | it "contains no beacon", ->
81 | expect(textEditorElement.
82 | querySelectorAll('span.beacon').length).toBe 0
83 | expect(textEditorElement.
84 | querySelectorAll('span.beacon').length).toBe 0
85 |
86 | # TODO: verify this one!
87 | xdescribe "when a custom match (jumpy default) is used", ->
88 | it "draws correct labels", ->
89 | labels = textEditor.getOverlayDecorations()
90 | expect(labels.length)
91 | .toBe NUM_TOTAL_WORDS
92 | expect(labels[0].getProperties().item.textContent).toBe 'aa'
93 | expect(labels[1].getProperties().item.textContent).toBe 'ab'
94 | expect(labels[82].getProperties().item.textContent).toBe 'de'
95 | expect(labels[83].getProperties().item.textContent).toBe 'df'
96 |
97 | # TODO: this needs to be rewritten for Jasmine 3
98 | xdescribe "when a custom match is used (camel case)", ->
99 | # Only read the jumpy.matchPattern once at initialization now.
100 | beforeEach ->
101 | atom.packages.deactivatePackage 'jumpy'
102 |
103 | it "draws correct labels and jumps appropriately", ->
104 | atom.config.set 'jumpy.matchPattern', '([A-Z]+([0-9a-z])*)|[a-z0-9]{2,}'
105 | activate = atom.packages.activatePackage 'jumpy'
106 |
107 | waitsForPromise ->
108 | activate
109 |
110 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
111 | labels = textEditor.getOverlayDecorations()
112 | expect(labels.length)
113 | .toBe NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES
114 | # BASE CASE WORDS:
115 | expect(labels[0].getProperties().item.textContent).toBe 'aa'
116 | expect(labels[1].getProperties().item.textContent).toBe 'ab'
117 | expect(labels[82].getProperties().item.textContent).toBe 'de'
118 | expect(labels[83].getProperties().item.textContent).toBe 'df'
119 |
120 | #CAMELS:
121 | keydown('e')
122 | keydown('a')
123 | cursorPosition = textEditor.getCursorBufferPosition()
124 | expect(cursorPosition.row).toBe 30
125 | expect(cursorPosition.column).toBe 4
126 |
127 | #UNDERSCORES:
128 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
129 | keydown('e')
130 | keydown('l')
131 | cursorPosition = textEditor.getCursorBufferPosition()
132 | expect(cursorPosition.row).toBe 32
133 | expect(cursorPosition.column).toBe 5
134 |
135 |
136 | describe "when customKeys is used", ->
137 | # Tests hot swapping of keys.
138 | # To confusing if this doesn't work for beginner Atom users without an Atom restart.
139 | beforeEach (done) ->
140 | atom.commands.dispatch textEditorElement, 'jumpy:toggle' # close default toggle from above before changing settings
141 | wait(done)
142 | beforeEach (done) ->
143 | atom.config.set 'jumpy.matchPattern', '([A-Z]+([0-9a-z])*)|[a-z0-9]{2,}'
144 | atom.config.set 'jumpy.customKeys', ['s', 'd', 'f', 'g', 'h', 'j', 'k', 'l'] # home keys skipping 'a'
145 | wait(done)
146 | beforeEach (done) ->
147 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
148 | wait(done)
149 | it "draws correct labels", ->
150 | labels = textEditor.getOverlayDecorations()
151 | expect(labels.length)
152 | .toBe NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES
153 |
154 | expect(labels[0].getProperties().item.textContent).toBe 'ss'
155 | expect(labels[1].getProperties().item.textContent).toBe 'sd'
156 | expect(labels[82].getProperties().item.textContent).toBe 'Ff'
157 | expect(labels[83].getProperties().item.textContent).toBe 'Fg'
158 |
--------------------------------------------------------------------------------
/spec/jumpy-single-letter-spec.coffee:
--------------------------------------------------------------------------------
1 | ### global
2 | atom
3 | jasmine describe xdescribe beforeEach afterEach it expect
4 | ###
5 | path = require 'path'
6 | {$} = require 'space-pen'
7 | {keydown} = require './helpers/keydown'
8 | {wait} = require './helpers/wait'
9 |
10 | describe "Jumpy", ->
11 | [workspaceElement, textEditorElement, textEditor] = []
12 |
13 | beforeEach ->
14 | atom.packages.activatePackage 'jumpy'
15 |
16 | beforeEach ->
17 | atom.packages.activatePackage 'status-bar'
18 |
19 | beforeEach ->
20 | atom.workspace.open 'test_text_single_letter.md'
21 |
22 | beforeEach ->
23 | # TODO: Abstract the following out, (DRY) --------------
24 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000
25 | atom.project.setPaths([path.join(__dirname, 'fixtures')])
26 | workspaceElement = atom.views.getView(atom.workspace)
27 | # @leedohm helped me with this idiom of workspace size.
28 | # He found it in the wrap-guide.
29 | workspaceElement.style.height = "5000px" # big enough
30 | workspaceElement.style.width = "5000px"
31 | jasmine.attachToDOM(workspaceElement)
32 | # TODO: Abstract the following out, (DRY) --------------
33 |
34 | textEditor = atom.workspace.getActiveTextEditor()
35 | textEditorElement = atom.views.getView(textEditor)
36 | textEditor.setCursorBufferPosition [1,1]
37 |
38 | beforeEach (done) ->
39 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
40 | wait(done)
41 |
42 | afterEach ->
43 | expect(atom.workspace.getActivePaneItem().isModified()).toBeFalsy()
44 | atom.workspace.destroy 'test_text_single_letter.md'
45 |
46 | # (least surprise + the label doesn't change to reflect that
47 | # only one needs to be hit)
48 | describe "when the jumpy:toggle event is triggered
49 | and first letter entered matches only one label", ->
50 | beforeEach (done) ->
51 | keydown('b')
52 | wait(done)
53 |
54 | it "doesn't jump until second letter is entered", ->
55 | expect(textEditorElement
56 | .classList.contains('jumpy-jump-mode')).toBe true
57 |
--------------------------------------------------------------------------------
/spec/jumpy-spec.coffee:
--------------------------------------------------------------------------------
1 | ### global
2 | atom
3 | jasmine describe xdescribe beforeEach afterEach it expect
4 | ###
5 | path = require 'path'
6 | {$} = require 'space-pen'
7 | {keydown} = require './helpers/keydown'
8 | {wait} = require './helpers/wait'
9 |
10 | NUM_ALPHA_TEST_WORDS = 26 * 3
11 | NUM_ENGLISH_TEXT = 8 - 2 #For a's that are only 1 character. *'s don't count.
12 | NUM_COLLAPSIBLE_WORDS = 19
13 | NUM_CAMEL_WORDS = 3
14 | NUM_TOTAL_WORDS =
15 | NUM_ALPHA_TEST_WORDS +
16 | NUM_ENGLISH_TEXT +
17 | NUM_COLLAPSIBLE_WORDS +
18 | NUM_CAMEL_WORDS
19 |
20 | NUM_CAMEL_SPECIFIC_MATCHES = 4 + 5 + 3
21 |
22 | getDecorationsArrayFromAllEditors = ->
23 | decorations = []
24 | atom.workspace.observeTextEditors (editor) ->
25 | currentTextEditorElement = atom.views.getView(editor)
26 | return if $(currentTextEditorElement).is ':not(:visible)'
27 |
28 | decorations = decorations.concat(editor.getOverlayDecorations())
29 | return decorations
30 |
31 | # Borrowed from: @lee-dohm
32 | # Public: Indicates whether an element has a command.
33 | #
34 | # * `element` An {HTMLElement} to search.
35 | # * `name` A {String} containing the command name.
36 | #
37 | # Returns a {Boolean} indicating if it has the given command.
38 | hasCommand = (element, name) ->
39 | commands = atom.commands.findCommands(target: element)
40 | found = true for command in commands when command.name is name
41 |
42 | found
43 |
44 | describe "Jumpy", ->
45 | [workspaceElement, textEditorElement, textEditor ] = []
46 |
47 | beforeEach ->
48 | atom.packages.activatePackage 'jumpy'
49 |
50 | beforeEach ->
51 | atom.packages.activatePackage 'status-bar'
52 |
53 | beforeEach ->
54 | atom.workspace.open 'test_text.md'
55 |
56 | beforeEach ->
57 | # TODO: Abstract the following out, (DRY) --------------
58 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000
59 | atom.project.setPaths([path.join(__dirname, 'fixtures')])
60 | workspaceElement = atom.views.getView(atom.workspace)
61 | # @leedohm helped me with this idiom of workspace size.
62 | # He found it in the wrap-guide.
63 | workspaceElement.style.height = "5000px" # big enough
64 | workspaceElement.style.width = "5000px"
65 | jasmine.attachToDOM(workspaceElement)
66 | # TODO: Abstract the following out, (DRY) --------------
67 |
68 | textEditor = atom.workspace.getActiveTextEditor()
69 | textEditorElement = atom.views.getView(textEditor)
70 | textEditor.setCursorBufferPosition [1,1]
71 |
72 | beforeEach (done) ->
73 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
74 | wait(done)
75 |
76 | afterEach ->
77 | expect(atom.workspace.getActivePaneItem().isModified()).toBeFalsy()
78 | atom.workspace.destroy 'test_text.md'
79 |
80 | describe 'activate', ->
81 | it 'creates the commands', ->
82 | expect(hasCommand(workspaceElement, 'jumpy:toggle')).toBeTruthy()
83 | expect(hasCommand(workspaceElement, 'jumpy:reset')).toBeTruthy()
84 | expect(hasCommand(workspaceElement, 'jumpy:clear')).toBeTruthy()
85 |
86 | describe 'deactivate', ->
87 | beforeEach ->
88 | atom.packages.deactivatePackage 'jumpy'
89 |
90 | it 'destroys the commands', ->
91 | expect(hasCommand(workspaceElement, 'jumpy:toggle')).toBeFalsy()
92 | expect(hasCommand(workspaceElement, 'jumpy:reset')).toBeFalsy()
93 | expect(hasCommand(workspaceElement, 'jumpy:clear')).toBeFalsy()
94 |
95 | describe "when the jumpy:toggle event is triggered", ->
96 | it "draws correct labels", ->
97 | decorations = textEditor.getOverlayDecorations()
98 | expect(decorations.length)
99 | .toBe NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES
100 | expect(decorations[0].getProperties().item.textContent).toBe 'aa'
101 | expect(decorations[1].getProperties().item.textContent).toBe 'ab'
102 | expect(decorations[82].getProperties().item.textContent).toBe 'de'
103 | expect(decorations[83].getProperties().item.textContent).toBe 'df'
104 | it "clears beacon effect", ->
105 | expect(textEditorElement.
106 | querySelectorAll('span.beacon').length).toBe 0
107 |
108 | describe "when the jumpy:clear event is triggered", ->
109 | beforeEach (done) ->
110 | atom.commands.dispatch workspaceElement, 'jumpy:clear'
111 | wait(done)
112 |
113 | it "clears labels", =>
114 | expect(textEditorElement
115 | .classList.contains('jumpy-jump-mode')).toBe false
116 | expect(textEditor.getOverlayDecorations()).toHaveLength 0
117 |
118 | describe "when the jumpy:toggle event is triggered
119 | and a click event is fired", ->
120 | beforeEach (done) ->
121 | textEditorElement.dispatchEvent new Event 'click'
122 | wait(done)
123 |
124 | it "jumpy is cleared", ->
125 | expect(textEditorElement.classList.contains('jumpy-jump-mode'))
126 | .toBe false
127 |
128 | xdescribe "when the jumpy:toggle event is triggered
129 | and a scroll event is fired", ->
130 | it "jumpy is cleared", ->
131 | # TODO: Finish test for scroll-up
132 | expect(textEditorElement.classList.contains('jumpy-jump-mode'))
133 | .toBe false
134 |
135 | # TODO: Finish test for scroll-down
136 | expect(textEditorElement.classList.contains('jumpy-jump-mode'))
137 | .toBe false
138 |
139 | # TODO: Finish test for scroll-left
140 | expect(textEditorElement.classList.contains('jumpy-jump-mode'))
141 | .toBe false
142 |
143 | # TODO: Finish test for scroll-right
144 | expect(textEditorElement.classList.contains('jumpy-jump-mode'))
145 | .toBe false
146 |
147 | describe "when the jumpy:toggle event is triggered
148 | and hotkeys are entered", ->
149 | beforeEach (done) ->
150 | keydown('a')
151 | wait(done)
152 | beforeEach (done) ->
153 | keydown('c')
154 | wait(done)
155 | it "jumpy is cleared", ->
156 | expect(textEditor.getOverlayDecorations().filter (d) ->
157 | d.properties.item.className == 'jumpy-label').toHaveLength 0
158 |
159 | describe "when the jumpy:toggle event is triggered
160 | and invalid hotkeys are entered", ->
161 | beforeEach (done) ->
162 | keydown('z')
163 | wait(done)
164 | beforeEach (done) ->
165 | keydown('z')
166 | wait(done)
167 | it "does nothing", ->
168 | cursorPosition = textEditor.getCursorBufferPosition()
169 | expect(cursorPosition.row).toBe 1
170 | expect(cursorPosition.column).toBe 1
171 |
172 | describe "when the jumpy:toggle event is triggered", ->
173 | it "loads 'jumpy-jump-mode'", ->
174 | expect(textEditorElement
175 | .classList.contains('jumpy-jump-mode')).toBeTruthy()
176 |
177 | describe "when the jumpy:toggle event is triggered
178 | and hotkeys are entered", ->
179 | beforeEach (done) ->
180 | keydown('a')
181 | wait(done)
182 | beforeEach (done) ->
183 | keydown('c')
184 | wait(done)
185 |
186 | it "jumps the cursor", ->
187 | cursorPosition = textEditor.getCursorBufferPosition()
188 | expect(cursorPosition.row).toBe 0
189 | expect(cursorPosition.column).toBe 6
190 | expect(textEditor.getSelectedText()).toBe ''
191 | it "clears jumpy mode", ->
192 | expect(textEditorElement.
193 | classList.contains('jumpy-jump-mode')).not.toBeTruthy()
194 |
195 | # Need to work on this one it's non deterministic!
196 | xdescribe "when the jumpy:toggle event is triggered
197 | and hotkeys are entered", ->
198 | beforeEach (done) ->
199 | atom.commands.dispatch workspaceElement, 'jumpy:clear'
200 | wait(done)
201 |
202 | beforeEach (done) ->
203 | textEditor.setCursorBufferPosition [23, 20]
204 | textEditor.foldBufferRow(22)
205 | wait(done)
206 |
207 | beforeEach (done) ->
208 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
209 | wait(done)
210 |
211 | beforeEach (done) ->
212 | keydown('d')
213 | wait(done)
214 |
215 | beforeEach (done) ->
216 | keydown('i')
217 | wait(done)
218 |
219 | beforeEach (done) ->
220 | cursorPosition = textEditor.getCursorScreenPosition()
221 | expect(cursorPosition.row).toBe 23
222 | expect(cursorPosition.column).toBe 2
223 | wait(done)
224 |
225 | beforeEach (done) ->
226 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
227 | wait(done)
228 |
229 | beforeEach (done) ->
230 | keydown('d')
231 | wait(done)
232 |
233 | beforeEach (done) ->
234 | keydown('h')
235 | wait(done)
236 |
237 | it "jumps the cursor in folded regions", ->
238 | cursorPosition = textEditor.getCursorScreenPosition()
239 | expect(cursorPosition.row).toBe 22
240 | expect(cursorPosition.column).toBe 0
241 |
242 | # TODO: Need to work on this it's non deterministic!
243 | xdescribe "when the jumpy:toggle event is triggered
244 | and hotkeys are entered in succession", ->
245 | beforeEach (done) ->
246 | keydown('a')
247 | wait(done)
248 | beforeEach (done) ->
249 | keydown('c')
250 | wait(done)
251 | beforeEach (done) ->
252 | atom.commands.dispatch workspaceElement, 'jumpy:toggle'
253 | wait(done)
254 | beforeEach (done) ->
255 | keydown('b')
256 | wait(done)
257 | beforeEach (done) ->
258 | keydown('e')
259 | wait(done)
260 | it "jumps the cursor twice", ->
261 | cursorPosition = textEditor.getCursorBufferPosition()
262 | expect(cursorPosition.row).toBe 6
263 | expect(cursorPosition.column).toBe 12
264 |
265 | describe "when the jumpy:toggle event is triggered
266 | and hotkeys are entered", ->
267 | beforeEach (done) ->
268 | keydown('a')
269 | wait(done)
270 | beforeEach (done) ->
271 | keydown('c')
272 | wait(done, 100) # only there for 150 ms
273 |
274 | it "the beacon animation class is added", ->
275 | expect(textEditorElement
276 | .querySelectorAll('.beacon').length)
277 | .toBe 1
278 |
279 | describe "when the jumpy:toggle event is triggered
280 | and hotkeys are entered", ->
281 | beforeEach (done) ->
282 | keydown('a')
283 | wait(done) # should be gone after 150 ms
284 | beforeEach (done) ->
285 | keydown('c')
286 | wait(done, 1000) # should be gone after 150 ms
287 |
288 | it "the beacon animation class is removed", ->
289 | expect(textEditorElement
290 | .querySelectorAll('.beacon').length)
291 | .toBe 0
292 |
293 | describe "when the jumpy:toggle event is triggered", ->
294 | it "updates the status bar", ->
295 | expect(document.querySelector('#status-bar-jumpy')
296 | .innerHTML.trim()).toBe 'Jumpy: Jump Mode! '
297 |
298 | describe "when the jumpy:clear event is triggered", ->
299 | beforeEach (done) ->
300 | atom.commands.dispatch workspaceElement, 'jumpy:clear'
301 | wait(done)
302 | it "clears the status bar", ->
303 | expect(document.querySelector('#status-bar-jumpy')).toBeNull()
304 |
305 | describe "when the jumpy:clear event is triggered", ->
306 | beforeEach (done) ->
307 | atom.commands.dispatch workspaceElement, 'jumpy:clear'
308 | wait(done)
309 | beforeEach (done) ->
310 | atom.commands.dispatch workspaceElement, 'jumpy:toggle'
311 | wait(done)
312 | it "does not prevent future status bar changes", ->
313 | expect(document.querySelector('#status-bar-jumpy .status')
314 | .innerHTML).toBe 'Jump Mode!'
315 |
316 | describe "when the keydown 'a' event is triggered", ->
317 | beforeEach (done) ->
318 | keydown('a')
319 | wait(done)
320 |
321 | it "updates the status bar with a", ->
322 | expect(document
323 | .querySelector '#status-bar-jumpy .status'
324 | .innerHTML).toBe 'a'
325 |
326 | # TODO: Need to work on this it's non deterministic!
327 | xit "removes all labels that don't begin with a", ->
328 | decorations = textEditor.getOverlayDecorations()
329 | relevantDecorations = decorations.filter (d) ->
330 | not d.getProperties().item.classList.contains 'irrelevant'
331 | expect(relevantDecorations).toHaveLength 26
332 |
333 | describe "when the jumpy:reset event is triggered", ->
334 | beforeEach (done) ->
335 | keydown('a')
336 | wait(done)
337 | beforeEach (done) ->
338 | atom.commands.dispatch textEditorElement, 'jumpy:reset'
339 | wait(done)
340 | beforeEach (done) ->
341 | keydown('a')
342 | wait(done)
343 | beforeEach (done) ->
344 | keydown('e')
345 | wait(done)
346 | it "clears first entered key and lets a new jump take place", ->
347 | cursorPosition = textEditor.getCursorBufferPosition()
348 | expect(cursorPosition.row).toBe 0
349 | expect(cursorPosition.column).toBe 12
350 |
351 | describe "when the jumpy:reset event is triggered", ->
352 | beforeEach (done) ->
353 | keydown('a')
354 | wait(done)
355 | beforeEach (done) ->
356 | atom.commands.dispatch textEditorElement, 'jumpy:reset'
357 | wait(done)
358 | it "updates the status bar", ->
359 | expect(document
360 | .querySelector('#status-bar-jumpy .status')
361 | .innerHTML).toBe 'Jump Mode!'
362 |
363 | describe "when the jumpy:reset event is triggered", ->
364 | beforeEach (done) ->
365 | keydown('a')
366 | wait(done)
367 | beforeEach (done) ->
368 | atom.commands.dispatch textEditorElement, 'jumpy:reset'
369 | wait(done)
370 | beforeEach (done) ->
371 | keydown('a')
372 | wait(done)
373 | beforeEach (done) ->
374 | atom.commands.dispatch textEditorElement, 'jumpy:reset'
375 | wait(done)
376 | beforeEach (done) ->
377 | atom.commands.dispatch textEditorElement, 'jumpy:clear'
378 | wait(done)
379 | beforeEach (done) ->
380 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
381 | wait(done)
382 | it "does not prevent next load's status", ->
383 | expect(document
384 | .querySelector('#status-bar-jumpy .status')
385 | .innerHTML).toBe 'Jump Mode!'
386 |
387 | describe "when the jumpy:reset event is triggered", ->
388 | beforeEach (done) ->
389 | keydown('a')
390 | wait(done)
391 | beforeEach (done) ->
392 | atom.commands.dispatch textEditorElement, 'jumpy:reset'
393 | wait(done)
394 | it "resets all labels even those that don't begin with a", ->
395 | decorations = textEditor.getOverlayDecorations()
396 | relevantDecorations = decorations.filter (d) ->
397 | not d.getProperties().item.classList.contains 'irrelevant'
398 | expect(relevantDecorations).toHaveLength NUM_TOTAL_WORDS +
399 | NUM_CAMEL_SPECIFIC_MATCHES
400 |
401 | describe "when a jump is performed", ->
402 | beforeEach (done) ->
403 | keydown('a')
404 | wait(done)
405 | beforeEach (done) ->
406 | keydown('a')
407 | wait(done)
408 | it "clears the status bar", ->
409 | expect(document.querySelector('#status-bar-jumpy')).toBeNull()
410 |
411 | # TODO: This does not currently test vim mode.
412 | describe "when the a text selection has begun
413 | before a jumpy:toggle event is triggered", ->
414 | beforeEach (done) ->
415 | keydown('a')
416 | keydown('a')
417 | wait(done)
418 |
419 | beforeEach (done) ->
420 | textEditor.selectRight()
421 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
422 | wait(done)
423 |
424 | beforeEach (done) ->
425 | keydown('a')
426 | keydown('e')
427 | wait(done)
428 |
429 | it "keeps the selection for subsequent jumps", ->
430 | # these were at the start, probably don't need them
431 | # atom.commands.dispatch textEditorElement, 'jumpy:clear'
432 | # atom.commands.dispatch textEditorElement, 'jumpy:toggle'
433 | expect(textEditor.getSelections()[0].getText()).toBe 'aa ab ac ad '
434 |
435 | # TODO: Fix this
436 | describe "when a character is entered that has no match", ->
437 | beforeEach (done) ->
438 | keydown('z')
439 | wait(done)
440 |
441 | it "displays a status bar error message", ->
442 | expect(document
443 | .getElementById 'status-bar-jumpy'
444 | .classList.contains 'no-match').toBeTruthy()
445 |
446 | describe "when a character is entered that has no match", ->
447 | beforeEach (done) ->
448 | keydown('z')
449 | wait(done)
450 | beforeEach (done) ->
451 | keydown('a')
452 | wait(done)
453 | it "eventually clears the status bar error message", ->
454 | expect(document
455 | .querySelector '#status-bar-jumpy'
456 | .classList.contains 'no-match').toBeFalsy()
457 | expect(document
458 | .querySelector '#status-bar-jumpy .status'
459 | .innerHTML == 'a').toBeTruthy()
460 |
461 | describe "when a character is entered that has no match", ->
462 | beforeEach (done) ->
463 | keydown('z') # ensure 2 z's with below's
464 | wait(done)
465 | beforeEach (done) ->
466 | keydown('z')
467 | wait(done)
468 | it "does not jump", ->
469 | cursorPosition = textEditor.getCursorBufferPosition()
470 | expect(cursorPosition.row).toBe 1
471 | expect(cursorPosition.column).toBe 1
472 | it "leaves the labels up", ->
473 | decorations = textEditor.getOverlayDecorations()
474 | relevantDecorations = decorations.filter (d) ->
475 | not d.getProperties().item.classList.contains 'irrelevant'
476 | expect(relevantDecorations.length > 0).toBeTruthy()
477 |
478 | # TODO: finish this
479 | xdescribe "when toggle is called with a split tab", ->
480 | it "continues to label consecutively", ->
481 | pane = atom.workspace.paneForItem(textEditor)
482 | pane.splitRight
483 | copyActiveItem: true
484 |
485 | # NOTE: This also ensures that I shouldn't have to clear the labels
486 | # In the test, but rather the code does that! (Because the test
487 | # setup does one toggle always)
488 | atom.commands.dispatch workspaceElement, 'jumpy:toggle'
489 |
490 | decorations = getDecorationsArrayFromAllEditors()
491 | expectedTotalNumberWith2Panes =
492 | (NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES) * 2
493 | expect(decorations).toHaveLength expectedTotalNumberWith2Panes
494 | # Beginning of first file
495 | expect(decorations[0].getProperties().item.textContent).toBe 'aa'
496 | expect(decorations[1].getProperties().item.textContent).toBe 'ab'
497 |
498 | # End of first file
499 | expect(decorations[116].getProperties().item.textContent).toBe 'em'
500 | expect(decorations[117].getProperties().item.textContent).toBe 'en'
501 |
502 | # Beginning of second file
503 | expect(decorations[118].getProperties().item.textContent).toBe 'eo'
504 | expect(decorations[119].getProperties().item.textContent).toBe 'ep'
505 |
506 | # Fix
507 | xdescribe "when toggle is called with 2 tabs open in same pane", ->
508 | beforeEach ->
509 | atom.workspace.open 'test_text2.md',
510 | activatePane: true # Just to be clear!
511 |
512 | beforeEach (done) ->
513 | # TODO: For this test case,
514 | # these 2 new instances *MIGHT* be crucial.
515 | # Or become crucial. I think it's best to leave these.
516 | currentTextEditor = atom.workspace.getActiveTextEditor()
517 | currentTextEditorElement = atom.views.getView(currentTextEditor)
518 |
519 | # This Should clear the first jumpy:toggle and re run it
520 | # now that we're on the 2nd file.
521 | atom.commands.dispatch currentTextEditorElement, 'jumpy:toggle'
522 | wait(done)
523 |
524 | it "continues to label consecutively", ->
525 | decorations = getDecorationsArrayFromAllEditors()
526 | expectedTotalNumberWith2TabsOpenInOnePane =
527 | (NUM_TOTAL_WORDS + NUM_CAMEL_SPECIFIC_MATCHES + 3)
528 | expect(decorations)
529 | .toHaveLength expectedTotalNumberWith2TabsOpenInOnePane
530 |
531 | describe "when a jump mode is enabled", ->
532 | beforeEach (done) ->
533 | atom.packages.activatePackage 'find-and-replace'
534 | wait(done, 2000)
535 |
536 | beforeEach (done) ->
537 | atom.commands.dispatch textEditorElement, 'find-and-replace:show'
538 | wait(done)
539 |
540 | it "clears when a find-and-replace mini pane is opened", ->
541 | expect(textEditorElement
542 | .classList.contains('jumpy-jump-mode')).toBe false
543 | expect(textEditor.getOverlayDecorations().filter (d) ->
544 | d.properties.item.className == 'jumpy-label').toHaveLength 0
545 | expect(workspaceElement
546 | .querySelectorAll('.find-and-replace')).toHaveLength 1
547 |
548 | describe "when a jump mode is enabled", ->
549 | beforeEach (done) ->
550 | atom.packages.activatePackage 'fuzzy-finder'
551 | wait(done, 2000)
552 |
553 | beforeEach (done) ->
554 | atom.commands.dispatch textEditorElement, 'fuzzy-finder:toggle-file-finder'
555 | wait(done)
556 |
557 | it "clears when a fuzzy-finder mini pane is opened", ->
558 | expect(textEditorElement
559 | .classList.contains('jumpy-jump-mode')).toBe false
560 | expect(textEditor.getOverlayDecorations()).toHaveLength 0
561 | expect(workspaceElement
562 | .querySelectorAll('.fuzzy-finder')).toHaveLength 1
563 |
564 | # TODO: This test doesn't work. Also, shouldn't need vim-mode-plus
565 | # This would need upgrading to Jasmine 3.
566 | xdescribe "when insert mode is used before jumping", ->
567 | activationPromise = []
568 | beforeEach ->
569 | activationPromise = atom.packages.activatePackage 'vim-mode-plus'
570 |
571 | it "does not leave the editor in a dirty / modified state", ->
572 | waitsForPromise ->
573 | activationPromise
574 |
575 | runs ->
576 | atom.commands.dispatch textEditorElement, 'jumpy:toggle' # turn off the initial from scaffolding
577 | atom.commands.dispatch textEditorElement, 'vim-mode-plus:activate-insert-mode'
578 | atom.commands.dispatch textEditorElement, 'jumpy:toggle'
579 | keydown('a', textEditorElement)
580 | keydown('a', textEditorElement)
581 | expect(textEditorElement
582 | .classList.contains('jumpy-jump-mode')).toBe false
583 | # Why don't these get added to the editor text?
584 | keydown('b', textEditorElement)
585 | keydown('b', textEditorElement)
586 | # **************************************************************
587 | # The the parent afterEach() handles expectations.
588 | # **************************************************************
589 |
--------------------------------------------------------------------------------
/styles/.atom-text-editor.less:
--------------------------------------------------------------------------------
1 | @import "ui-variables";
2 |
3 | #status-bar-jumpy {
4 | color: @text-color-info;
5 | &.no-match,
6 | &.no-match .status {
7 | color: @text-color-warning;
8 | }
9 | }
10 |
11 | .jumpy-label {
12 | &.irrelevant { // This is a marker to indicate not applicable (when 1 key is entered/known).
13 | visibility: hidden;
14 | }
15 | color: @base-background-color;
16 | background: @text-color;
17 | position: absolute;
18 | vertical-align: bottom;
19 | transform: translate(0, -100%);
20 | padding: .15em;
21 | border-radius: @component-border-radius;
22 | font-family: @font-family;
23 | &.high-contrast {
24 | background-color: @background-color-success;
25 | }
26 | &.tab-label {
27 | margin-left: 10px;
28 | }
29 | }
30 |
31 | @import "beacon.less";
32 |
--------------------------------------------------------------------------------
/styles/beacon.less:
--------------------------------------------------------------------------------
1 | // Original code / inspiration: http://codepen.io/daless14/pen/ELFgj/
2 | // Greatly modified of course...
3 |
4 | @import "ui-variables";
5 |
6 | .beacon {
7 | display: block;
8 | position: absolute;
9 | border-radius: 1px;
10 | width: 2px;
11 | height: 2px;
12 | z-index: -1;
13 | -webkit-animation: ripple .085s;
14 | content: '';
15 | top: -6px;
16 | &.tab-beacon {
17 | // TODO: Really work needs to be done to trigger *after* tab switch
18 | left: 18px;
19 | top: -15px;
20 | }
21 | }
22 | @-webkit-keyframes ripple {
23 | 0% {
24 | box-shadow:0 0 0 0 transparent,
25 | 0 0 0 0 transparent,
26 | 0 0 0 0 transparent,
27 | 0 0 0 0 transparent;
28 | }
29 | 5% {
30 | box-shadow:0 0 0 0 @background-color-error,
31 | 0 0 0 0 rgba(255,255,255,0.5),
32 | 0 0 0 0 @background-color-error,
33 | 0 0 0 0 rgba(0,0,0,0.1);
34 | }
35 | 100% {
36 | box-shadow:0 0 4px 24px @background-color-error,
37 | 0 0 0 0 transparent,
38 | 0 0 0 0 transparent,
39 | 0 0 0 0 transparent;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/styles/theme-atom-material-ui.atom-text-editor.less:
--------------------------------------------------------------------------------
1 | // need special handling for atom-material theme.
2 | .jumpy-label {
3 | color: @text-color;
4 | background-color: @base-background-color;
5 |
6 | &.high-contrast {
7 | color: @text-color;
8 | background-color: @background-color-success;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tests/Tests.elm:
--------------------------------------------------------------------------------
1 | module Tests exposing (suite)
2 |
3 | import Expect exposing (Expectation)
4 | import Fuzz exposing (Fuzzer, int, list, string)
5 | import StateMachine exposing (Model, Msg(..), resetKeys, update)
6 | import Test exposing (..)
7 |
8 |
9 | suite : Test
10 | suite =
11 | let
12 | initial =
13 | { active = False
14 | , keysEntered = ""
15 | , lastJumped = ""
16 | , labels = []
17 | , status = ""
18 | }
19 |
20 | labels =
21 | [ "aa", "ab", "ac" ]
22 | in
23 | describe "Jumpy"
24 | [ describe "reset"
25 | [ test "resets keys (too many keys)" <|
26 | \_ ->
27 | { initial | keysEntered = "hello" }
28 | |> resetKeys
29 | |> Expect.equal initial
30 | , test "resets keys (1 letter)" <|
31 | \_ ->
32 | { initial | keysEntered = "h" }
33 | |> resetKeys
34 | |> Expect.equal initial
35 | , test "resets keys (empty key)" <|
36 | \_ ->
37 | { initial | keysEntered = "" }
38 | |> resetKeys
39 | |> Expect.equal initial
40 | , test "reset does not set message if off" <|
41 | \_ ->
42 | initial
43 | |> update Reset
44 | |> Tuple.first
45 | |> Expect.equal initial
46 | ]
47 | , describe "adding a key"
48 | [ test "does not add any non matched keys" <|
49 | \_ ->
50 | initial
51 | -- add "Z"
52 | |> update (LoadLabels labels)
53 | |> Tuple.first
54 | |> update (KeyEntered 90)
55 | |> Tuple.first
56 | |> Expect.equal
57 | { initial
58 | | labels = labels
59 | , active = True
60 | , status = "Jumpy: No Match! 😞
"
61 | }
62 | , test "adds a matched key" <|
63 | \_ ->
64 | initial
65 | -- add "a"
66 | |> update (LoadLabels labels)
67 | |> Tuple.first
68 | |> update (KeyEntered 97)
69 | |> Tuple.first
70 | |> Expect.equal
71 | { initial
72 | | labels = labels
73 | , active = True
74 | , keysEntered = "a"
75 | , status = "Jumpy: a
"
76 | }
77 | ]
78 | , describe "turning on/off"
79 | [ test "reports active True after load" <|
80 | \_ ->
81 | initial
82 | |> update (LoadLabels labels)
83 | |> Tuple.first
84 | |> Expect.equal
85 | { initial
86 | | labels = labels
87 | , active = True
88 | , keysEntered = ""
89 | , status = "Jumpy: Jump Mode!
"
90 | }
91 | , test "reports active False after an exit" <|
92 | \_ ->
93 | initial
94 | |> update (LoadLabels labels)
95 | |> Tuple.first
96 | |> update Exit
97 | |> Tuple.first
98 | |> Expect.equal
99 | { initial
100 | | labels = labels
101 | , active = False
102 | }
103 | ]
104 | ]
105 |
--------------------------------------------------------------------------------