diff options
Diffstat (limited to 'src/shared/commands')
| -rw-r--r-- | src/shared/commands/complete.js | 84 | ||||
| -rw-r--r-- | src/shared/commands/index.js | 3 | ||||
| -rw-r--r-- | src/shared/commands/parsers.js | 59 | 
3 files changed, 146 insertions, 0 deletions
| diff --git a/src/shared/commands/complete.js b/src/shared/commands/complete.js new file mode 100644 index 0000000..0bdbab8 --- /dev/null +++ b/src/shared/commands/complete.js @@ -0,0 +1,84 @@ +import * as tabs from 'background/tabs'; +import * as histories from 'background/histories'; + +const getOpenCompletions = (command, keywords, searchConfig) => { +  return histories.getCompletions(keywords).then((pages) => { +    let historyItems = pages.map((page) => { +      return { +        caption: page.title, +        content: command + ' ' + page.url, +        url: page.url +      }; +    }); +    let engineNames = Object.keys(searchConfig.engines); +    let engineItems = engineNames.filter(name => name.startsWith(keywords)) +      .map(name => ({ +        caption: name, +        content: command + ' ' + name +      })); + +    let completions = []; +    if (engineItems.length > 0) { +      completions.push({ +        name: 'Search Engines', +        items: engineItems +      }); +    } +    if (historyItems.length > 0) { +      completions.push({ +        name: 'History', +        items: historyItems +      }); +    } +    return completions; +  }); +}; + +const getCompletions = (line, settings) => { +  let typedWords = line.trim().split(/ +/); +  let typing = ''; +  if (!line.endsWith(' ')) { +    typing = typedWords.pop(); +  } + +  if (typedWords.length === 0) { +    return Promise.resolve([]); +  } +  let name = typedWords.shift(); +  let keywords = typedWords.concat(typing).join(' '); + +  switch (name) { +  case 'o': +  case 'open': +  case 't': +  case 'tabopen': +  case 'w': +  case 'winopen': +    return getOpenCompletions(name, keywords, settings.search); +  case 'b': +  case 'buffer': +    return tabs.getCompletions(keywords).then((gotTabs) => { +      let items = gotTabs.map((tab) => { +        return { +          caption: tab.title, +          content: name + ' ' + tab.title, +          url: tab.url, +          icon: tab.favIconUrl +        }; +      }); +      return [ +        { +          name: 'Buffers', +          items: items +        } +      ]; +    }); +  } +  return Promise.resolve([]); +}; + +const complete = (line, settings) => { +  return getCompletions(line, settings); +}; + +export default complete; diff --git a/src/shared/commands/index.js b/src/shared/commands/index.js new file mode 100644 index 0000000..78cb4df --- /dev/null +++ b/src/shared/commands/index.js @@ -0,0 +1,3 @@ +import complete from './complete'; + +export { complete }; diff --git a/src/shared/commands/parsers.js b/src/shared/commands/parsers.js new file mode 100644 index 0000000..fb37d2a --- /dev/null +++ b/src/shared/commands/parsers.js @@ -0,0 +1,59 @@ +const normalizeUrl = (args, searchConfig) => { +  let concat = args.join(' '); +  try { +    return new URL(concat).href; +  } catch (e) { +    if (concat.includes('.') && !concat.includes(' ')) { +      return 'http://' + concat; +    } +    let query = concat; +    let template = searchConfig.engines[ +      searchConfig.default +    ]; +    for (let key in searchConfig.engines) { +      if (args[0] === key) { +        query = args.slice(1).join(' '); +        template = searchConfig.engines[key]; +      } +    } +    return template.replace('{}', encodeURIComponent(query)); +  } +}; + +const mustNumber = (v) => { +  let num = Number(v); +  if (isNaN(num)) { +    throw new Error('Not number: ' + v); +  } +  return num; +}; + +const parseSetOption = (word, types) => { +  let [key, value] = word.split('='); +  if (value === undefined) { +    value = !key.startsWith('no'); +    key = value ? key : key.slice(2); +  } +  let type = types[key]; +  if (!type) { +    throw new Error('Unknown property: ' + key); +  } +  if (type === 'boolean' && typeof value !== 'boolean' || +       type !== 'boolean' && typeof value === 'boolean') { +    throw new Error('Invalid argument: ' + word); +  } + +  switch (type) { +  case 'string': return [key, value]; +  case 'number': return [key, mustNumber(value)]; +  case 'boolean': return [key, value]; +  } +}; + +const parseCommandLine = (line) => { +  let words = line.trim().split(/ +/); +  let name = words.shift(); +  return [name, words]; +}; + +export { normalizeUrl, parseCommandLine, parseSetOption }; | 
