From 8099fb286243b0ff7855e02bf817e24097d7a361 Mon Sep 17 00:00:00 2001 From: Belar Date: Sun, 26 Jan 2020 18:03:02 +0100 Subject: [PATCH] fix: items without definition being active for click fixes: #11 --- lib/clickProvider.js | 27 ++++++++++---- lib/goToDefinition.js | 37 +++++++++++-------- lib/main.js | 10 +++-- spec/atom-ide-click-spec.js | 73 ------------------------------------ spec/click-provider-spec.js | 74 +++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 100 deletions(-) delete mode 100644 spec/atom-ide-click-spec.js create mode 100644 spec/click-provider-spec.js diff --git a/lib/clickProvider.js b/lib/clickProvider.js index caeb75c..4cdb893 100644 --- a/lib/clickProvider.js +++ b/lib/clickProvider.js @@ -1,21 +1,32 @@ 'use babel'; +import { goToDefinition, getDefinitions } from './goToDefinition'; + export class ClickProvider { constructor(options) { - this.clickHandler = options.clickHandler; + this.providerRegistry = options.providerRegistry; this.suggestionForWordHandler = this.suggestionForWordHandler.bind(this); this.getProvider = this.getProvider.bind(this); } - suggestionForWordHandler(textEditor, text, range) { - const targetValue = text && text.trim(); - if (!targetValue) return; + async suggestionForWordHandler(editor, text, range) { + const { start: position } = range; + const definitions = await getDefinitions(this.providerRegistry, { + editor, + position, + }); + const hasDefinitions = definitions && !!definitions[0]; - return { - range, - callback: () => this.clickHandler(), - }; + const suggestion = hasDefinitions + ? { + range, + callback: () => + goToDefinition(this.providerRegistry, { editor, definitions }), + } + : false; + + return suggestion; } getProvider() { diff --git a/lib/goToDefinition.js b/lib/goToDefinition.js index c2fe1eb..94041f7 100644 --- a/lib/goToDefinition.js +++ b/lib/goToDefinition.js @@ -1,22 +1,15 @@ 'use babel'; -async function goToDefinition(providerRegistry) { - if (!providerRegistry) { +export async function goToDefinition( + providerRegistry, + { editor, definitions: knownDefinitions } = {} +) { + if (!editor) { return; } - const editor = atom.workspace.getActiveTextEditor(); - const provider = providerRegistry.getProviderForEditor(editor); - - const position = editor.getCursorBufferPosition(); - const result = await provider.getDefinition(editor, position); - - if (!result) { - atom.notifications.addError('Sorry, no definitions found.'); - return; - } - - const { definitions } = result; + const definitions = + knownDefinitions || (await getDefinitions(providerRegistry, { editor })); if (!definitions || definitions.length === 0) { atom.notifications.addError('Sorry, no definitions found.'); @@ -45,4 +38,18 @@ async function goToDefinition(providerRegistry) { } } -export default goToDefinition; +export async function getDefinitions( + providerRegistry, + { editor, position: targetPosition } = {} +) { + if (!providerRegistry || !editor) { + return; + } + + const position = targetPosition || editor.getCursorBufferPosition(); + + const provider = providerRegistry.getProviderForEditor(editor); + const result = await provider.getDefinition(editor, position); + + return result && result.definitions; +} diff --git a/lib/main.js b/lib/main.js index 6f269a9..5165b45 100644 --- a/lib/main.js +++ b/lib/main.js @@ -2,14 +2,14 @@ import { CompositeDisposable } from 'atom'; import { install as installPackageDependencies } from 'atom-package-deps'; -import goToDefinition from './goToDefinition'; +import { goToDefinition } from './goToDefinition'; import createProviderRegistry from './providerRegistry'; import { ClickProvider } from './clickProvider'; function package() { const providerRegistry = createProviderRegistry(); const clickProvider = new ClickProvider({ - clickHandler: () => goToDefinition(providerRegistry), + providerRegistry, }); let subscriptions; @@ -18,8 +18,10 @@ function package() { subscriptions.add( atom.commands.add('atom-workspace', { - 'atom-ide-go-to-definition:go-to-definition': () => - goToDefinition(providerRegistry), + 'atom-ide-go-to-definition:go-to-definition': () => { + const editor = atom.workspace.getActiveTextEditor(); + goToDefinition(providerRegistry, { editor }); + }, }) ); diff --git a/spec/atom-ide-click-spec.js b/spec/atom-ide-click-spec.js deleted file mode 100644 index dddd23e..0000000 --- a/spec/atom-ide-click-spec.js +++ /dev/null @@ -1,73 +0,0 @@ -'use babel'; - -import { ClickProvider } from '../lib/clickProvider'; - -// Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. -// -// To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` -// or `fdescribe`). Remove the `f` to unfocus the block. - -let clickProvider; -const clickHandlers = { - goToDefinitions: () => {}, -}; - -describe('AtomIdeClick', () => { - beforeEach(function() { - spyOn(atom.config, 'get').andCallFake(function(value) { - const config = { - 'atom-ide-definitions.clickGrammarScopes': [], - 'atom-ide-definitions.clickPriority': 0, - }; - return config[value]; - }); - - spyOn(clickHandlers, 'goToDefinitions'); - - clickProvider = new ClickProvider({ - clickHandler: clickHandlers.goToDefinitions, - }); - }); - - it('verify provider result', () => { - const provider = clickProvider.getProvider(); - - expect(provider.priority).toEqual(0); - expect(provider.grammarScopes).toBeNull(); - expect(typeof provider.getSuggestionForWord).toEqual('function'); - }); - - it('verify suggestionForWord result', () => { - const suggestionForWord = clickProvider.suggestionForWordHandler( - 'FakeEditor', - 'FakeText', - 'FakeRange' - ); - - expect(suggestionForWord.range).toEqual('FakeRange'); - expect(typeof suggestionForWord.callback).toEqual('function'); - }); - - it('verify suggestionForWord handler stops if there is no text value', () => { - const suggestionForWord = clickProvider.suggestionForWordHandler( - 'FakeEditor', - '', - 'FakeRange' - ); - - expect(suggestionForWord).toBeUndefined(); - }); - - it('verify suggestionForWord callback assignment', () => { - const suggestionForWord = clickProvider.suggestionForWordHandler( - 'FakeEditor', - 'FakeText', - 'FakeRange' - ); - - expect(typeof suggestionForWord.callback).toEqual('function'); - - suggestionForWord.callback(); - expect(clickHandlers.goToDefinitions).toHaveBeenCalled(); - }); -}); diff --git a/spec/click-provider-spec.js b/spec/click-provider-spec.js new file mode 100644 index 0000000..f159b29 --- /dev/null +++ b/spec/click-provider-spec.js @@ -0,0 +1,74 @@ +'use babel'; + +import { ClickProvider } from '../lib/clickProvider'; +import * as goToDefinition from '../lib/goToDefinition'; + +// Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. +// +// To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` +// or `fdescribe`). Remove the `f` to unfocus the block. + +describe('click provider', () => { + const editor = atom.workspace.getActiveTextEditor(); + const providerRegistry = { + getProviderForEditor: () => {}, + }; + const definitions = [ + { + language: 'Lang', + path: '/path/to/file', + position: { row: 0, column: 0 }, + range: { start: { row: 0, column: 0 }, end: { row: 0, column: 1 } }, + }, + ]; + const word = 'foo'; + const range = { start: { row: 0, column: 0 }, end: { row: 0, column: 3 } }; + + let clickProvider; + beforeEach(function() { + spyOn(atom.config, 'get').andCallFake(function(value) { + const config = { + 'atom-ide-definitions.clickGrammarScopes': [], + 'atom-ide-definitions.clickPriority': 0, + }; + return config[value]; + }); + + clickProvider = new ClickProvider(providerRegistry); + }); + + it('verify provider structure', () => { + const provider = clickProvider.getProvider(); + + expect(provider.priority).toEqual(0); + expect(provider.grammarScopes).toBeNull(); + expect(typeof provider.getSuggestionForWord).toEqual('function'); + }); + + it('getSuggestionForWord returns range and callback if definition is available', async () => { + const provider = clickProvider.getProvider(); + spyOn(goToDefinition, 'getDefinitions').andCallFake(() => definitions); + + const suggestionForWord = await provider.getSuggestionForWord( + editor, + word, + range + ); + + expect(suggestionForWord.range).toEqual(range); + expect(typeof suggestionForWord.callback).toEqual('function'); + }); + + it('getSuggestionForWord returns false if definition is unavailable', async () => { + const provider = clickProvider.getProvider(); + spyOn(goToDefinition, 'getDefinitions').andCallFake(() => null); + + const suggestionForWord = await provider.getSuggestionForWord( + editor, + word, + range + ); + + expect(suggestionForWord).toEqual(false); + }); +});