Skip to content

Commit

Permalink
Add support for non repo root Haskell projects. (#463)
Browse files Browse the repository at this point in the history
In my use case we keep several Haskell projects in one repository (monorepo approach). This PR extends search to modules relative to Haskell project root (not git root). It is detected by cutting current path on `src`, `lib`, `app`, or `test`. Also adds navigation in test suites.
  • Loading branch information
kfigiela authored and Stefan Buck committed Jun 1, 2018
1 parent 45e3be4 commit 6fbe466
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 22 deletions.
2 changes: 1 addition & 1 deletion assets/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "OctoLinker",
"version": "4.17.1",
"version": "4.18.0",
"manifest_version": 2,
"author": "Stefan Buck",
"description": "",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@octolinker/core",
"version": "4.17.1",
"version": "4.18.0",
"description": "OctoLinker browser extension core",
"repository": "https://github.com/octolinker/octolinker/tree/master/packages/core",
"license": "MIT",
Expand Down
34 changes: 25 additions & 9 deletions packages/plugin-haskell/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
import assert from 'assert';
import githubSearch from '@octolinker/resolver-github-search';
import { hoogleSearch } from '@octolinker/resolver-hoogle-search';
import Haskell from '../index';

describe('haskell', () => {
const path = '/user/repo/blob/d6/lib/plugins/javascript.js';
const target = 'Foo.Bar';
it('resolves links - top level project', () => {
const path = '/user/repo/blob/v0.1/src/Main.hs';
const target = 'Foo.Bar';

it('resolves links', () => {
assert.deepEqual(Haskell.resolve(path, [target]), [
'{BASE_URL}/user/repo/blob/master/src/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/master/lib/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/master/Foo/Bar.hs',
githubSearch({ path, target }).toString(),
'https://hackage.haskell.org/package/base/docs/Foo-Bar.html',
'{BASE_URL}/user/repo/blob/v0.1/src/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/lib/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/app/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/test/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/Foo/Bar.hs',
hoogleSearch({ target }).toString(),
]);
});

it('resolves links - monorepo project', () => {
const path =
'/user/repo/blob/v0.1/projects/project-in-monorepo/src/Main.hs';
const target = 'Foo.Bar';

assert.deepEqual(Haskell.resolve(path, [target]), [
'{BASE_URL}/user/repo/blob/v0.1/projects/project-in-monorepo/src/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/projects/project-in-monorepo/lib/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/projects/project-in-monorepo/app/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/projects/project-in-monorepo/test/Foo/Bar.hs',
'{BASE_URL}/user/repo/blob/v0.1/projects/project-in-monorepo/Foo/Bar.hs',
hoogleSearch({ target }).toString(),
]);
});
});
19 changes: 9 additions & 10 deletions packages/plugin-haskell/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { HASKELL_IMPORT } from '@octolinker/helper-grammar-regex-collection';
import githubSearch from '@octolinker/resolver-github-search';
import { hoogleSearch } from '@octolinker/resolver-hoogle-search';

export default {
name: 'Haskell',

resolve(path, [target]) {
const [, user, repo] = path.split('/');
const filePath = target.replace(/\./g, '/');
const basePath = path.split(/\/(src|lib|app|test)\//)[0];

return [
`{BASE_URL}/${user}/${repo}/blob/master/src/${filePath}.hs`,
`{BASE_URL}/${user}/${repo}/blob/master/lib/${filePath}.hs`,
`{BASE_URL}/${user}/${repo}/blob/master/${filePath}.hs`,
githubSearch({ path, target: `${filePath}.hs` }),
`https://hackage.haskell.org/package/base/docs/${target.replace(
/\./g,
'-',
)}.html`,
`{BASE_URL}${basePath}/src/${filePath}.hs`,
`{BASE_URL}${basePath}/lib/${filePath}.hs`,
`{BASE_URL}${basePath}/app/${filePath}.hs`,
`{BASE_URL}${basePath}/test/${filePath}.hs`,
`{BASE_URL}${basePath}/${filePath}.hs`,
hoogleSearch({ target }),
];
},

Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-haskell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "MIT",
"main": "./index.js",
"dependencies": {
"@octolinker/resolver-github-search": "1.0.0",
"@octolinker/resolver-hoogle-search": "1.0.0",
"@octolinker/helper-grammar-regex-collection": "1.0.0"
}
}
47 changes: 47 additions & 0 deletions packages/resolver-hoogle-search/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import assert from 'assert';
import { hoogleSearch } from '../index';

describe('hoogle-search', () => {
const target = 'Data.Typeable';
let response;
beforeAll(() => {
global.fetch = jest.fn().mockImplementation(() =>
Promise.resolve({
json() {
return response;
},
}),
);
});

it('returns a function', () => {
assert.deepEqual(typeof hoogleSearch({ target }), 'function');
});

it('calls the hoogle search api', () => {
hoogleSearch({ target })();

expect(global.fetch).toBeCalledWith(
'https://hoogle.haskell.org/?hoogle=Data.Typeable%20is:module%20is:exact&mode=json',
);
});

it('returns url', async () => {
response = [
{
url:
'https://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Typeable.html',
},
];

expect(await hoogleSearch({ target })()).toBe(
'https://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Typeable.html',
);
});

it('returns null when no results', async () => {
response = [];

expect(await hoogleSearch({ target })()).toBe(null);
});
});
29 changes: 29 additions & 0 deletions packages/resolver-hoogle-search/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function handleResponse(hoogleSearchUrl, target, response) {
const checkExact = function(item) {
return item.url.includes(`docs/${target.replace(/\./g, '-')}.html`);
};

if (response.length > 0) {
// Multiple packages may provide the same module (see Crypto.MAC.HMAC)
// We redirect to doc if there is only exact match
// Otherwise, we redirect to hoogle search to avoid confucion
const exactMatches = response.filter(checkExact);
if (exactMatches.length === 1) {
return exactMatches[0].url;
}
return hoogleSearchUrl;
}
return null;
}

export function hoogleSearch({ target }) {
const query = `${encodeURIComponent(target)}%20is:module%20is:exact`;
const hoogleSearchUrl = `https://hoogle.haskell.org/?hoogle=${query}&mode=json`;

return async function doHoogleSearch() {
const response = await fetch(hoogleSearchUrl);
const json = await response.json();

return handleResponse(hoogleSearchUrl, target, json);
};
}
11 changes: 11 additions & 0 deletions packages/resolver-hoogle-search/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@octolinker/resolver-hoogle-search",
"version": "1.0.0",
"description": "",
"repository": "https://github.com/octolinker/octolinker/tree/master/packages/resolver-hoogle-search",
"license": "MIT",
"main": "./index.js",
"dependencies": {
"@octolinker/helper-settings": "1.0.0"
}
}

0 comments on commit 6fbe466

Please sign in to comment.