Skip to content

Commit

Permalink
fix: add zsh autocomplete setup and file permissions instructions to …
Browse files Browse the repository at this point in the history
…completion:install (#6882)

* fix: fixed bugs in completion

Co-authored-by: Dylan Spyer <dylanspyer@gmail.com>

* fix: add zsh autocompletion setup and file permissions instructions to completion:install

Co-authored-by: Dylan Spyer <dylanspyer@gmail.com>

* fix: update completion

Co-authored-by: Dylan Spyer <dylanspyer@gmail.com>

* fix: remove obselete ts-expect-error in command-helpers

Co-authored-by: Dylan Spyer <dylanspyer@gmail.com>

* fix: update constant imports in completion:install test file

Co-authored-by: Dylan Spyer <dylanspyer@gmail.com>

---------

Co-authored-by: Dylan Spyer <dylanspyer@gmail.com>
  • Loading branch information
benhancock and dylanspyer authored Oct 28, 2024
1 parent 89e814d commit 75c0e7b
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 3 deletions.
50 changes: 47 additions & 3 deletions src/commands/completion/completion.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import fs from 'fs'
import { homedir } from 'os'
import { dirname, join } from 'path'
import { fileURLToPath } from 'url'
import inquirer from 'inquirer'

import { OptionValues } from 'commander'
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'tabt... Remove this comment to see the full error message
import { install, uninstall } from 'tabtab'

import { generateAutocompletion } from '../../lib/completion/index.js'
import { error } from '../../utils/command-helpers.js'
import {
error,
log,
chalk,
checkFileForLine,
TABTAB_CONFIG_LINE,
AUTOLOAD_COMPINIT,
} from '../../utils/command-helpers.js'
import BaseCommand from '../base-command.js'

const completer = join(dirname(fileURLToPath(import.meta.url)), '../../lib/completion/script.js')
Expand All @@ -20,13 +30,47 @@ export const completionGenerate = async (options: OptionValues, command: BaseCom
}

generateAutocompletion(parent)

await install({
name: parent.name(),
completer,
})
const zshConfigFilepath = join(process.env.HOME || homedir(), '.zshrc')

if (
fs.existsSync(zshConfigFilepath) &&
checkFileForLine(zshConfigFilepath, TABTAB_CONFIG_LINE) &&
!checkFileForLine(zshConfigFilepath, AUTOLOAD_COMPINIT)
) {
log(`To enable Tabtab autocompletion with zsh, the following line may need to be added to your ~/.zshrc:`)
log(chalk.bold.cyan(`\n${AUTOLOAD_COMPINIT}\n`))
const { compinitAdded } = await inquirer.prompt([
{
type: 'confirm',
name: 'compinitAdded',
message: `Would you like to add it?`,
default: true,
},
])
if (compinitAdded) {
await fs.readFile(zshConfigFilepath, 'utf8', (err, data) => {
const updatedZshFile = AUTOLOAD_COMPINIT + '\n' + data

console.log(`Completion for ${parent.name()} successful installed!`)
fs.writeFileSync(zshConfigFilepath, updatedZshFile, 'utf8')
})

log('Successfully added compinit line to .zshrc')
}
}

log(`Completion for ${parent.name()} successfully installed!`)

if (process.platform !== 'win32') {
log("\nTo ensure proper functionality, you'll need to set appropriate file permissions.")
log(chalk.bold('Add executable permissions by running the following command:'))
log(chalk.bold.cyan(`\nchmod +x ${completer}\n`))
} else {
log(`\nTo ensure proper functionality, you may need to set appropriate file permissions to ${completer}.`)
}
}

export const completionUninstall = async (options: OptionValues, command: BaseCommand) => {
Expand Down
14 changes: 14 additions & 0 deletions src/utils/command-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { once } from 'events'
import os from 'os'
import fs from 'fs'
import process from 'process'
import { format, inspect } from 'util'

Expand Down Expand Up @@ -313,3 +314,16 @@ export interface APIError extends Error {
status: number
message: string
}

export const checkFileForLine = (filename: string, line: string) => {
let filecontent = ''
try {
filecontent = fs.readFileSync(filename, 'utf8')
} catch (error_) {
error(error_)
}
return !!filecontent.match(`${line}`)
}

export const TABTAB_CONFIG_LINE = '[[ -f ~/.config/tabtab/__tabtab.zsh ]] && . ~/.config/tabtab/__tabtab.zsh || true'
export const AUTOLOAD_COMPINIT = 'autoload -U compinit; compinit'
79 changes: 79 additions & 0 deletions tests/integration/commands/completion/completion-install.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { describe, expect, test, beforeAll, afterAll } from 'vitest'
import fs from 'fs'
import { rm } from 'fs/promises'
import { temporaryDirectory } from 'tempy'
import { handleQuestions, CONFIRM, DOWN, NO, answerWithValue } from '../../utils/handle-questions.js'
import execa from 'execa'
import { cliPath } from '../../utils/cli-path.js'
import { join } from 'path'
import { TABTAB_CONFIG_LINE, AUTOLOAD_COMPINIT } from '../../../../src/utils/command-helpers.js'

describe('completion:install command', () => {
let tempDir
let zshConfigPath
let options

beforeAll(() => {
tempDir = temporaryDirectory()
zshConfigPath = join(tempDir, '.zshrc')
options = { cwd: tempDir, env: { HOME: tempDir } }
})

afterAll(async () => {
await rm(tempDir, { force: true, recursive: true })
})

test.skipIf(process.env.SHELL !== '/bin/zsh')(
'should add compinit to .zshrc when user confirms prompt',
async (t) => {
fs.writeFileSync(zshConfigPath, TABTAB_CONFIG_LINE)
const childProcess = execa(cliPath, ['completion:install'], options)

handleQuestions(childProcess, [
{
question: 'Which Shell do you use ?',
answer: answerWithValue(DOWN),
},
{
question: 'We will install completion to ~/.zshrc, is it ok ?',
answer: CONFIRM,
},
{
question: 'Would you like to add it?',
answer: CONFIRM,
},
])

await childProcess
const content = fs.readFileSync(zshConfigPath, 'utf8')
expect(content).toContain(AUTOLOAD_COMPINIT)
},
)

test.skipIf(process.env.SHELL !== '/bin/zsh')(
'should not add compinit to .zshrc when user does not confirm prompt',
async (t) => {
fs.writeFileSync(zshConfigPath, TABTAB_CONFIG_LINE)
const childProcess = execa(cliPath, ['completion:install'], options)

handleQuestions(childProcess, [
{
question: 'Which Shell do you use ?',
answer: answerWithValue(DOWN),
},
{
question: 'We will install completion to ~/.zshrc, is it ok ?',
answer: CONFIRM,
},
{
question: 'Would you like to add it?',
answer: answerWithValue(NO),
},
])

await childProcess
const content = fs.readFileSync(zshConfigPath, 'utf8')
expect(content).not.toContain(AUTOLOAD_COMPINIT)
},
)
})

0 comments on commit 75c0e7b

Please # to comment.