Merge branch 'history-completion'

jh-changes
Shin'ya Ueoka 7 years ago
commit 8a8222158c
  1. 1
      .eslintrc
  2. 2
      README.md
  3. 3
      manifest.json
  4. 23
      src/actions/command.js
  5. 84
      src/background/histories.js
  6. 5
      src/console/console.scss

@ -22,6 +22,7 @@
"comma-dangle": "off", "comma-dangle": "off",
"consistent-return": "off", "consistent-return": "off",
"default-case": "off", "default-case": "off",
"dot-location": ["error", "property"],
"function-paren-newline": "off", "function-paren-newline": "off",
"id-length": "off", "id-length": "off",
"indent": ["error", 2], "indent": ["error", 2],

@ -21,7 +21,7 @@ Firefox by WebExtensions API.
- [ ] open command - [ ] open command
- [x] open a link - [x] open a link
- [ ] search by keywords with engined - [ ] search by keywords with engined
- [ ] complete URLs from history - [x] complete URLs from history
- [ ] complete keywords for search - [ ] complete keywords for search
- [x] tabs navigation - [x] tabs navigation
- [x] select a tabs by keyboard - [x] select a tabs by keyboard

@ -16,7 +16,8 @@
}, },
"permissions": [ "permissions": [
"sessions", "sessions",
"tabs" "tabs",
"history"
], ],
"web_accessible_resources": [ "web_accessible_resources": [
"build/console.html" "build/console.html"

@ -1,4 +1,5 @@
import * as tabs from '../background/tabs'; import * as tabs from '../background/tabs';
import * as histories from '../background/histories';
import * as consoleActions from './console'; import * as consoleActions from './console';
const normalizeUrl = (string) => { const normalizeUrl = (string) => {
@ -39,9 +40,11 @@ const bufferCommand = (keywords) => {
const doCommand = (name, remaining) => { const doCommand = (name, remaining) => {
switch (name) { switch (name) {
case 'o':
case 'open': case 'open':
// TODO use search engined and pass keywords to them // TODO use search engined and pass keywords to them
return openCommand(normalizeUrl(remaining)); return openCommand(normalizeUrl(remaining));
case 't':
case 'tabopen': case 'tabopen':
return tabopenCommand(normalizeUrl(remaining)); return tabopenCommand(normalizeUrl(remaining));
case 'b': case 'b':
@ -53,6 +56,26 @@ const doCommand = (name, remaining) => {
const getCompletions = (command, keywords) => { const getCompletions = (command, keywords) => {
switch (command) { switch (command) {
case 'o':
case 'open':
case 't':
case 'tabopen':
return histories.getCompletions(keywords).then((pages) => {
let items = pages.map((page) => {
return {
caption: page.title,
content: page.url,
url: page.url
};
});
return [
{
name: 'History',
items
}
];
});
case 'b':
case 'buffer': case 'buffer':
return tabs.getCompletions(keywords).then((gotTabs) => { return tabs.getCompletions(keywords).then((gotTabs) => {
let items = gotTabs.map((tab) => { let items = gotTabs.map((tab) => {

@ -0,0 +1,84 @@
const filterHttp = (items) => {
const httpsHosts = items
.filter(item => item[1].protocol === 'https:')
.map(item => item[1].host);
const httpsHostSet = new Set(httpsHosts);
return items.filter(
item => !(item[1].protocol === 'http:' && httpsHostSet.has(item[1].host))
);
};
const filterEmptyTitle = (items) => {
return items.filter(item => item[0].title && item[0].title !== '');
};
const filterClosedPath = (items) => {
const allSimplePaths = items
.filter(item => item[1].hash === '' && item[1].search === '')
.map(item => item[1].origin + item[1].pathname);
const allSimplePathSet = new Set(allSimplePaths);
return items.filter(
item => !(item[1].hash === '' && item[1].search === '' &&
(/\/$/).test(item[1].pathname) &&
allSimplePathSet.has(
(item[1].origin + item[1].pathname).replace(/\/$/, '')
)
)
);
};
const reduceByPathname = (items, min) => {
let hash = {};
for (let item of items) {
let pathname = item[1].origin + item[1].pathname;
if (!hash[pathname]) {
hash[pathname] = item;
} else if (hash[pathname][1].href.length > item[1].href.length) {
hash[pathname] = item;
}
}
let filtered = Object.values(hash);
if (filtered.length < min) {
return items;
}
return filtered;
};
const reduceByOrigin = (items, min) => {
let hash = {};
for (let item of items) {
let origin = item[1].origin;
if (!hash[origin]) {
hash[origin] = item;
} else if (hash[origin][1].href.length > item[1].href.length) {
hash[origin] = item;
}
}
let filtered = Object.values(hash);
if (filtered.length < min) {
return items;
}
return filtered;
};
const getCompletions = (keyword) => {
return browser.history.search({
text: keyword,
startTime: 0,
}).then((historyItems) => {
return [historyItems.map(item => [item, new URL(item.url)])]
.map(filterEmptyTitle)
.map(filterHttp)
.map(filterClosedPath)
.map(items => reduceByPathname(items, 10))
.map(items => reduceByOrigin(items, 10))
.map(items => items
.sort((x, y) => x[0].visitCount < y[0].visitCount)
.slice(0, 10)
.map(item => item[0])
.sort((x, y) => x.url > y.url)
)[0];
});
};
export { getCompletions };

@ -50,11 +50,16 @@ body {
&-caption { &-caption {
display: inline-block; display: inline-block;
width: 40%; width: 40%;
text-overflow: ellipsis;
overflow: hidden;
} }
&-url { &-url {
display: inline-block; display: inline-block;
color: green; color: green;
width: 60%;
text-overflow: ellipsis;
overflow: hidden;
} }
} }
} }