Merge branch 'history-completion'
This commit is contained in:
commit
8a8222158c
6 changed files with 116 additions and 2 deletions
|
@ -22,6 +22,7 @@
|
|||
"comma-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
"default-case": "off",
|
||||
"dot-location": ["error", "property"],
|
||||
"function-paren-newline": "off",
|
||||
"id-length": "off",
|
||||
"indent": ["error", 2],
|
||||
|
|
|
@ -21,7 +21,7 @@ Firefox by WebExtensions API.
|
|||
- [ ] open command
|
||||
- [x] open a link
|
||||
- [ ] search by keywords with engined
|
||||
- [ ] complete URLs from history
|
||||
- [x] complete URLs from history
|
||||
- [ ] complete keywords for search
|
||||
- [x] tabs navigation
|
||||
- [x] select a tabs by keyboard
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
},
|
||||
"permissions": [
|
||||
"sessions",
|
||||
"tabs"
|
||||
"tabs",
|
||||
"history"
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
"build/console.html"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as tabs from '../background/tabs';
|
||||
import * as histories from '../background/histories';
|
||||
import * as consoleActions from './console';
|
||||
|
||||
const normalizeUrl = (string) => {
|
||||
|
@ -39,9 +40,11 @@ const bufferCommand = (keywords) => {
|
|||
|
||||
const doCommand = (name, remaining) => {
|
||||
switch (name) {
|
||||
case 'o':
|
||||
case 'open':
|
||||
// TODO use search engined and pass keywords to them
|
||||
return openCommand(normalizeUrl(remaining));
|
||||
case 't':
|
||||
case 'tabopen':
|
||||
return tabopenCommand(normalizeUrl(remaining));
|
||||
case 'b':
|
||||
|
@ -53,6 +56,26 @@ const doCommand = (name, remaining) => {
|
|||
|
||||
const getCompletions = (command, keywords) => {
|
||||
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':
|
||||
return tabs.getCompletions(keywords).then((gotTabs) => {
|
||||
let items = gotTabs.map((tab) => {
|
||||
|
|
84
src/background/histories.js
Normal file
84
src/background/histories.js
Normal file
|
@ -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 {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-url {
|
||||
display: inline-block;
|
||||
color: green;
|
||||
width: 60%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue