2017-08-04 19:51:01 +02:00
import { app } from './app.js' ;
import { Note } from 'lib/models/note.js' ;
import { Folder } from 'lib/models/folder.js' ;
2017-08-04 18:02:43 +02:00
import { Tag } from 'lib/models/tag.js' ;
2017-08-04 19:51:01 +02:00
import { cliUtils } from './cli-utils.js' ;
2017-08-04 18:02:43 +02:00
import { _ } from 'lib/locale.js' ;
import fs from 'fs-extra' ;
import os from 'os' ;
2017-08-04 19:51:01 +02:00
import yargParser from 'yargs-parser' ;
2017-08-22 19:57:35 +02:00
function autocompletionFileContent ( appName , alias ) {
2017-08-04 18:02:43 +02:00
let content = fs . readFileSync ( _ _dirname + '/autocompletion_template.txt' , 'utf8' ) ;
content = content . replace ( /\|__APPNAME__\|/g , appName ) ;
2017-08-22 19:57:35 +02:00
if ( ! alias ) alias = 'joplin_alias_support_is_disabled' ;
content = content . replace ( /\|__APPALIAS__\|/g , alias ) ;
2017-08-04 18:02:43 +02:00
return content ;
}
2017-08-22 19:57:35 +02:00
function autocompletionScriptPath ( profileDir ) {
return profileDir + '/autocompletion.sh' ;
}
async function installAutocompletionFile ( appName , profileDir ) {
2017-08-04 18:02:43 +02:00
if ( process . env . SHELL . indexOf ( 'bash' ) < 0 ) {
let error = new Error ( _ ( 'Only Bash is currently supported for autocompletion.' ) ) ;
error . code = 'shellNotSupported' ;
throw error ;
}
2017-08-22 19:57:35 +02:00
const alias = await cliUtils . promptInput ( _ ( 'Autocompletion can be made to work with an alias too (such as a one-letter command like "j").\nIf you would like to enable this, please type the alias now (leave it empty for no alias):' ) ) ;
const content = autocompletionFileContent ( appName , alias ) ;
const filePath = autocompletionScriptPath ( profileDir ) ;
2017-08-04 18:02:43 +02:00
fs . writeFileSync ( filePath , content ) ;
2017-08-19 22:56:28 +02:00
console . info ( _ ( 'Created autocompletion script "%s".' , filePath ) ) ;
2017-08-04 18:02:43 +02:00
const bashProfilePath = os . homedir ( ) + '/.bashrc' ;
let bashrcContent = fs . readFileSync ( bashProfilePath , 'utf8' ) ;
const lineToAdd = 'source ' + filePath ;
if ( bashrcContent . indexOf ( lineToAdd ) >= 0 ) {
2017-08-19 22:56:28 +02:00
console . info ( _ ( 'Autocompletion script is already present in "%s".' , bashProfilePath ) ) ;
2017-08-04 18:02:43 +02:00
} else {
bashrcContent += "\n" + lineToAdd + "\n" ;
fs . writeFileSync ( bashProfilePath , bashrcContent ) ;
2017-08-19 22:56:28 +02:00
console . info ( _ ( 'Added autocompletion to "%s".' , bashProfilePath ) ) ;
2017-08-04 18:02:43 +02:00
}
2017-08-22 19:57:35 +02:00
if ( alias ) {
if ( bashrcContent . indexOf ( 'alias ' + alias + '=' ) >= 0 ) {
console . info ( _ ( 'Alias is already set in "%s".' , bashProfilePath ) ) ;
} else {
const l = 'alias ' + alias + '=' + appName ;
bashrcContent += "\n" + l + "\n" ;
fs . writeFileSync ( bashProfilePath , bashrcContent ) ;
console . info ( _ ( 'Added alias to "%s".' , bashProfilePath ) ) ;
}
}
2017-08-04 18:02:43 +02:00
2017-08-22 19:57:35 +02:00
console . info ( _ ( "IMPORTANT: run the following command to initialise autocompletion in the current shell:\nsource '%s'" , filePath ) ) ;
2017-08-04 18:02:43 +02:00
}
2017-08-04 19:51:01 +02:00
async function handleAutocompletion ( autocompletion ) {
let args = autocompletion . line . slice ( ) ;
args . splice ( 0 , 1 ) ;
let current = autocompletion . current - 1 ;
2017-08-04 18:02:43 +02:00
const currentWord = args [ current ] ? args [ current ] : '' ;
2017-08-04 19:51:01 +02:00
// Auto-complete the command name
if ( current == 0 ) {
const metadata = await app ( ) . commandMetadata ( ) ;
let commandNames = [ ] ;
for ( let n in metadata ) {
if ( ! metadata . hasOwnProperty ( n ) ) continue ;
const md = metadata [ n ] ;
if ( md . hidden ) continue ;
if ( currentWord == n ) return [ n ] ;
if ( n . indexOf ( currentWord ) === 0 ) commandNames . push ( n ) ;
}
return commandNames ;
}
const commandName = args [ 0 ] ;
const metadata = await app ( ) . commandMetadata ( ) ;
const md = metadata [ commandName ] ;
const options = md && md . options ? md . options : [ ] ;
// Auto-complete the command options
2017-08-04 18:02:43 +02:00
if ( currentWord ) {
const includeLongs = currentWord . length == 1 ? currentWord . substr ( 0 , 1 ) == '-' : currentWord . substr ( 0 , 2 ) == '--' ;
const includeShorts = currentWord . length <= 2 && currentWord . substr ( 0 , 1 ) == '-' && currentWord . substr ( 0 , 2 ) != '--' ;
if ( includeLongs || includeShorts ) {
const output = [ ] ;
for ( let i = 0 ; i < options . length ; i ++ ) {
const flags = cliUtils . parseFlags ( options [ i ] [ 0 ] ) ;
const long = flags . long ? '--' + flags . long : null ;
const short = flags . short ? '-' + flags . short : null ;
if ( includeLongs && long && long . indexOf ( currentWord ) === 0 ) output . push ( long ) ;
if ( includeShorts && short && short . indexOf ( currentWord ) === 0 ) output . push ( short ) ;
}
return output ;
2017-08-04 19:51:01 +02:00
}
}
// Auto-complete the command arguments
let argIndex = - 1 ;
for ( let i = 0 ; i < args . length ; i ++ ) {
const w = args [ i ] ;
if ( i == 0 || w . indexOf ( '-' ) == 0 ) {
continue ;
}
argIndex ++ ;
}
if ( argIndex < 0 ) return [ ] ;
let cmdUsage = yargParser ( md . usage ) [ '_' ] ;
cmdUsage . splice ( 0 , 1 ) ;
if ( cmdUsage . length <= argIndex ) return [ ] ;
let argName = cmdUsage [ argIndex ] ;
argName = cliUtils . parseCommandArg ( argName ) . name ;
2017-08-04 18:02:43 +02:00
if ( argName == 'note' || argName == 'note-pattern' ) {
2017-08-04 19:51:01 +02:00
if ( ! app ( ) . currentFolder ( ) ) return [ ] ;
const notes = await Note . previews ( app ( ) . currentFolder ( ) . id , { titlePattern : currentWord + '*' } ) ;
return notes . map ( ( n ) => n . title ) ;
}
if ( argName == 'notebook' ) {
const folders = await Folder . search ( { titlePattern : currentWord + '*' } ) ;
return folders . map ( ( n ) => n . title ) ;
}
2017-08-04 18:02:43 +02:00
if ( argName == 'tag' ) {
let tags = await Tag . search ( { titlePattern : currentWord + '*' } ) ;
return tags . map ( ( n ) => n . title ) ;
}
if ( argName == 'tag-command' ) {
return filterList ( [ 'add' , 'remove' , 'list' ] , currentWord ) ;
}
if ( argName == 'todo-command' ) {
return filterList ( [ 'toggle' , 'clear' ] , currentWord ) ;
}
2017-08-04 19:11:10 +02:00
if ( argName == 'command' ) {
const commands = await app ( ) . commandNames ( ) ;
return this . filterList ( commands , currentWord ) ;
}
2017-08-04 19:51:01 +02:00
return [ ] ;
}
2017-08-04 18:02:43 +02:00
function filterList ( list , currentWord ) {
let output = [ ] ;
for ( let i = 0 ; i < list . length ; i ++ ) {
if ( list [ i ] . indexOf ( currentWord ) !== 0 ) continue ;
output . push ( list [ i ] ) ;
}
return output ;
}
2017-08-22 19:57:35 +02:00
export { handleAutocompletion , installAutocompletionFile , autocompletionScriptPath } ;