- Snippets: Start empty, add via right-click context menu on selected text - Export/Import: Save and load snippets as JSON files with merge/replace options - Tab Renaming: Double-click tabs to rename them - Session Persistence: Tabs and their names are saved and restored on app restart - Delete Snippets: Remove unwanted snippets from the panel - Auto-suggestions: Comprehensive command database with 400+ commands - Includes Git, Docker, Kubernetes, NPM, Python, Ruby, Rust, Go, and more - Suggestions from command history, common commands, and system PATH - Updates in real-time as you type - Press Tab to accept suggestions - Fixed tab content persistence when switching between tabs - Fixed tab bar layout and positioning - All features fully functional and tested
331 lines
14 KiB
Swift
331 lines
14 KiB
Swift
import Foundation
|
|
|
|
// MARK: - Command History Manager
|
|
class HistoryManager {
|
|
static let shared = HistoryManager()
|
|
private var history: [String] = []
|
|
private let maxHistory = 1000
|
|
private var systemCommands: Set<String> = []
|
|
private var commonCommands: [String] = [
|
|
// Basic Unix/Linux commands
|
|
"ls", "ls -la", "ls -lah", "ls -ltr", "ls -lh", "ls -R",
|
|
"cd", "cd ..", "cd ~", "cd -", "cd /",
|
|
"pwd", "mkdir", "mkdir -p", "rmdir",
|
|
"rm", "rm -f", "rm -rf", "rm -i",
|
|
"cp", "cp -r", "cp -i", "cp -v",
|
|
"mv", "mv -i", "mv -v",
|
|
"cat", "less", "more", "head", "head -n", "tail", "tail -f", "tail -n",
|
|
"touch", "file", "stat",
|
|
|
|
// Text processing
|
|
"grep", "grep -r", "grep -i", "grep -v", "grep -n", "grep -A", "grep -B",
|
|
"egrep", "fgrep", "sed", "awk", "cut", "sort", "uniq", "wc", "wc -l",
|
|
"diff", "diff -u", "patch", "tr", "fmt", "fold", "expand", "unexpand",
|
|
|
|
// File search and find
|
|
"find", "find . -name", "find . -type f", "find . -type d", "find . -size",
|
|
"locate", "updatedb", "which", "whereis", "whatis", "apropos",
|
|
|
|
// File permissions
|
|
"chmod", "chmod +x", "chmod 755", "chmod 644", "chmod -R",
|
|
"chown", "chown -R", "chgrp", "umask",
|
|
|
|
// Process management
|
|
"ps", "ps aux", "ps -ef", "ps aux | grep", "pgrep", "pkill",
|
|
"top", "htop", "kill", "kill -9", "killall", "nice", "renice",
|
|
"bg", "fg", "jobs", "nohup", "screen", "tmux",
|
|
|
|
// System info
|
|
"uname", "uname -a", "hostname", "uptime", "whoami", "who", "w", "last",
|
|
"date", "cal", "df", "df -h", "du", "du -sh", "du -h", "free", "free -h",
|
|
"lsblk", "mount", "umount", "fdisk", "lsusb", "lspci", "dmesg",
|
|
|
|
// Network commands
|
|
"ping", "ping -c", "traceroute", "netstat", "netstat -an", "netstat -tulpn",
|
|
"ss", "ss -tulpn", "ifconfig", "ip", "ip addr", "ip route",
|
|
"curl", "curl -O", "curl -L", "wget", "wget -O", "wget -c",
|
|
"ssh", "ssh -i", "ssh -p", "scp", "scp -r", "sftp", "rsync", "rsync -av",
|
|
"nc", "nmap", "dig", "nslookup", "host", "whois",
|
|
|
|
// Archive and compression
|
|
"tar", "tar -xvf", "tar -cvf", "tar -xzvf", "tar -czvf", "tar -xjvf", "tar -cjvf",
|
|
"zip", "zip -r", "unzip", "unzip -l", "gzip", "gunzip", "bzip2", "bunzip2",
|
|
"xz", "unxz", "7z", "rar", "unrar",
|
|
|
|
// Git commands (comprehensive)
|
|
"git", "git init", "git clone", "git clone --depth 1",
|
|
"git status", "git status -s",
|
|
"git add", "git add .", "git add -A", "git add -p", "git add -u",
|
|
"git commit", "git commit -m", "git commit -am", "git commit --amend",
|
|
"git push", "git push origin", "git push -u origin", "git push --force", "git push --tags",
|
|
"git pull", "git pull origin", "git pull --rebase",
|
|
"git fetch", "git fetch origin", "git fetch --all", "git fetch --prune",
|
|
"git merge", "git merge --no-ff", "git merge --squash",
|
|
"git rebase", "git rebase -i", "git rebase --continue", "git rebase --abort",
|
|
"git branch", "git branch -a", "git branch -d", "git branch -D", "git branch -m",
|
|
"git checkout", "git checkout -b", "git checkout -", "git checkout --",
|
|
"git switch", "git switch -c",
|
|
"git log", "git log --oneline", "git log --graph", "git log --all --graph --oneline",
|
|
"git diff", "git diff --staged", "git diff HEAD", "git diff --cached",
|
|
"git stash", "git stash pop", "git stash list", "git stash apply", "git stash drop",
|
|
"git remote", "git remote -v", "git remote add origin", "git remote set-url origin",
|
|
"git tag", "git tag -a", "git tag -d", "git push --tags",
|
|
"git reset", "git reset --hard", "git reset --soft", "git reset HEAD",
|
|
"git revert", "git cherry-pick", "git bisect",
|
|
"git clean", "git clean -fd", "git rm", "git mv",
|
|
"git show", "git blame", "git reflog",
|
|
"git config", "git config --global user.name", "git config --global user.email",
|
|
|
|
// GitHub CLI
|
|
"gh", "gh repo clone", "gh repo create", "gh pr create", "gh pr list", "gh issue create",
|
|
|
|
// NPM/Node.js
|
|
"npm", "npm init", "npm init -y",
|
|
"npm install", "npm i", "npm install --save", "npm install --save-dev",
|
|
"npm uninstall", "npm update", "npm outdated",
|
|
"npm start", "npm run", "npm run build", "npm run dev", "npm run test",
|
|
"npm test", "npm run lint", "npm run format",
|
|
"npm list", "npm list -g", "npm search", "npm info",
|
|
"npm publish", "npm version", "npm audit", "npm audit fix",
|
|
"node", "node -v", "npx", "npx create-react-app", "npx create-next-app",
|
|
|
|
// Yarn
|
|
"yarn", "yarn init", "yarn add", "yarn add --dev", "yarn remove",
|
|
"yarn install", "yarn upgrade", "yarn start", "yarn build", "yarn test",
|
|
"yarn run", "yarn global add",
|
|
|
|
// pnpm
|
|
"pnpm", "pnpm install", "pnpm add", "pnpm remove", "pnpm run",
|
|
|
|
// Python
|
|
"python", "python3", "python -m", "python -c",
|
|
"pip", "pip install", "pip install -r requirements.txt", "pip uninstall",
|
|
"pip list", "pip freeze", "pip freeze > requirements.txt", "pip show",
|
|
"pip3", "pip3 install",
|
|
"python -m venv", "python3 -m venv venv",
|
|
"source venv/bin/activate", "deactivate",
|
|
"python -m pip install --upgrade pip",
|
|
"pytest", "pytest -v", "python -m pytest",
|
|
|
|
// Ruby/Rails
|
|
"ruby", "ruby -v", "gem", "gem install", "gem list", "gem update",
|
|
"bundle", "bundle install", "bundle update", "bundle exec",
|
|
"rails", "rails new", "rails server", "rails console", "rails generate",
|
|
"rake", "rake db:migrate", "rake db:seed",
|
|
|
|
// Docker (comprehensive)
|
|
"docker", "docker --version",
|
|
"docker ps", "docker ps -a", "docker ps -q",
|
|
"docker images", "docker images -a",
|
|
"docker run", "docker run -it", "docker run -d", "docker run -p", "docker run --rm",
|
|
"docker start", "docker stop", "docker restart", "docker pause", "docker unpause",
|
|
"docker rm", "docker rm -f", "docker rmi", "docker rmi -f",
|
|
"docker exec", "docker exec -it", "docker exec -it bash", "docker exec -it sh",
|
|
"docker logs", "docker logs -f", "docker logs --tail",
|
|
"docker build", "docker build -t", "docker build --no-cache",
|
|
"docker pull", "docker push", "docker tag",
|
|
"docker inspect", "docker stats", "docker top",
|
|
"docker network", "docker network ls", "docker network create", "docker network rm",
|
|
"docker volume", "docker volume ls", "docker volume create", "docker volume rm",
|
|
"docker system prune", "docker system prune -a",
|
|
"docker-compose", "docker-compose up", "docker-compose up -d", "docker-compose down",
|
|
"docker-compose build", "docker-compose logs", "docker-compose ps",
|
|
|
|
// Kubernetes
|
|
"kubectl", "kubectl version", "kubectl cluster-info",
|
|
"kubectl get", "kubectl get pods", "kubectl get services", "kubectl get deployments",
|
|
"kubectl get nodes", "kubectl get namespaces", "kubectl get all",
|
|
"kubectl describe", "kubectl describe pod", "kubectl describe service",
|
|
"kubectl logs", "kubectl logs -f", "kubectl logs --tail",
|
|
"kubectl exec", "kubectl exec -it", "kubectl exec -it -- /bin/bash",
|
|
"kubectl apply", "kubectl apply -f", "kubectl delete", "kubectl delete -f",
|
|
"kubectl create", "kubectl edit", "kubectl scale", "kubectl rollout",
|
|
"kubectl port-forward", "kubectl proxy",
|
|
|
|
// Terraform
|
|
"terraform", "terraform init", "terraform plan", "terraform apply", "terraform destroy",
|
|
"terraform fmt", "terraform validate", "terraform output",
|
|
|
|
// AWS CLI
|
|
"aws", "aws s3 ls", "aws s3 cp", "aws s3 sync", "aws ec2 describe-instances",
|
|
"aws configure", "aws sts get-caller-identity",
|
|
|
|
// Package managers
|
|
"brew", "brew install", "brew update", "brew upgrade", "brew list", "brew search",
|
|
"brew uninstall", "brew info", "brew doctor", "brew cleanup",
|
|
"apt", "apt update", "apt upgrade", "apt install", "apt remove", "apt search",
|
|
"apt-get", "apt-get update", "apt-get upgrade", "apt-get install",
|
|
"yum", "yum install", "yum update", "yum remove", "yum search",
|
|
"dnf", "dnf install", "dnf update", "dnf remove",
|
|
|
|
// Rust/Cargo
|
|
"cargo", "cargo new", "cargo init", "cargo build", "cargo run", "cargo test",
|
|
"cargo check", "cargo clean", "cargo update", "rustc", "rustup",
|
|
|
|
// Go
|
|
"go", "go run", "go build", "go test", "go get", "go mod init", "go mod tidy",
|
|
"go fmt", "go vet", "go install",
|
|
|
|
// Java/Maven/Gradle
|
|
"java", "javac", "jar", "mvn", "mvn clean", "mvn install", "mvn test", "mvn package",
|
|
"gradle", "gradle build", "gradle test", "gradle run",
|
|
|
|
// Database
|
|
"mysql", "mysql -u root -p", "mysqldump", "psql", "mongo", "redis-cli",
|
|
"sqlite3",
|
|
|
|
// Editors
|
|
"vim", "vi", "nano", "emacs", "code", "subl", "atom",
|
|
|
|
// Shell/Environment
|
|
"echo", "echo $PATH", "export", "env", "printenv", "set", "unset",
|
|
"alias", "unalias", "source", ".", "exec", "eval",
|
|
"history", "clear", "reset", "exit", "logout",
|
|
|
|
// Help/Documentation
|
|
"man", "help", "info", "tldr", "--help", "-h",
|
|
|
|
// Misc utilities
|
|
"xargs", "tee", "yes", "seq", "bc", "expr", "factor", "sleep",
|
|
"watch", "time", "timeout", "at", "cron", "crontab", "crontab -e",
|
|
"make", "cmake", "gcc", "g++", "clang",
|
|
"ln", "ln -s", "readlink", "basename", "dirname",
|
|
"md5sum", "sha256sum", "shasum", "openssl",
|
|
"jq", "yq", "xmllint", "base64",
|
|
|
|
// macOS specific
|
|
"open", "pbcopy", "pbpaste", "say", "caffeinate", "defaults",
|
|
"diskutil", "softwareupdate", "system_profiler",
|
|
|
|
// Common patterns and combinations
|
|
"sudo !!", "sudo -i", "sudo su", "su -",
|
|
"ls -la | grep", "ps aux | grep", "history | grep",
|
|
"find . -name '*.log'", "find . -type f -name",
|
|
"tar -xzvf file.tar.gz", "tar -czvf archive.tar.gz",
|
|
"chmod +x script.sh", "./script.sh",
|
|
"cat file.txt | grep", "tail -f /var/log/syslog",
|
|
"df -h | grep", "du -sh *", "du -sh * | sort -h",
|
|
"netstat -tulpn | grep", "lsof -i :8080",
|
|
"kill -9 $(ps aux | grep process | awk '{print $2}')",
|
|
"export PATH=$PATH:/usr/local/bin",
|
|
"source ~/.bashrc", "source ~/.zshrc",
|
|
"git log --oneline --graph --all --decorate",
|
|
"docker run -it --rm ubuntu bash",
|
|
"kubectl get pods -A", "kubectl get pods -o wide",
|
|
"npm install && npm start",
|
|
"python -m http.server", "python3 -m http.server 8000"
|
|
]
|
|
|
|
init() {
|
|
loadHistory()
|
|
loadSystemCommands()
|
|
}
|
|
|
|
private func loadSystemCommands() {
|
|
// Load commands from PATH in background
|
|
DispatchQueue.global(qos: .background).async { [weak self] in
|
|
var commands = Set<String>()
|
|
|
|
// Get PATH directories
|
|
let pathString = ProcessInfo.processInfo.environment["PATH"] ?? ""
|
|
let paths = pathString.split(separator: ":").map(String.init)
|
|
|
|
// Common directories to check
|
|
let commonPaths = ["/usr/bin", "/bin", "/usr/local/bin", "/opt/homebrew/bin"]
|
|
let allPaths = Set(paths + commonPaths)
|
|
|
|
for path in allPaths {
|
|
if let contents = try? FileManager.default.contentsOfDirectory(atPath: path) {
|
|
commands.formUnion(contents)
|
|
}
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
self?.systemCommands = commands
|
|
NSLog("Loaded \(commands.count) system commands for suggestions")
|
|
}
|
|
}
|
|
}
|
|
|
|
func addCommand(_ command: String) {
|
|
let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !trimmed.isEmpty else { return }
|
|
|
|
// Don't add duplicate consecutive commands
|
|
if history.last != trimmed {
|
|
history.append(trimmed)
|
|
}
|
|
|
|
if history.count > maxHistory {
|
|
history.removeFirst()
|
|
}
|
|
saveHistory()
|
|
}
|
|
|
|
func searchHistory(_ query: String) -> [String] {
|
|
if query.isEmpty {
|
|
return Array(history.reversed().prefix(50))
|
|
}
|
|
|
|
// Fuzzy search - match commands containing the query
|
|
let results = history.reversed().filter { command in
|
|
command.localizedCaseInsensitiveContains(query)
|
|
}
|
|
|
|
return Array(results.prefix(50))
|
|
}
|
|
|
|
func getRecentCommands(_ count: Int = 10) -> [String] {
|
|
return Array(history.reversed().prefix(count))
|
|
}
|
|
|
|
func getSuggestion(for partial: String) -> String? {
|
|
guard !partial.isEmpty else { return nil }
|
|
|
|
let trimmed = partial.trimmingCharacters(in: .whitespaces)
|
|
|
|
// Priority 1: Recent history (most relevant)
|
|
if let historyMatch = history.reversed().first(where: { command in
|
|
command.hasPrefix(trimmed) && command != trimmed
|
|
}) {
|
|
return historyMatch
|
|
}
|
|
|
|
// Priority 2: Common commands with arguments
|
|
if let commonMatch = commonCommands.first(where: { command in
|
|
command.hasPrefix(trimmed) && command != trimmed
|
|
}) {
|
|
return commonMatch
|
|
}
|
|
|
|
// Priority 3: System commands (just the command name)
|
|
// Only suggest if we're typing a single word (no spaces yet)
|
|
if !trimmed.contains(" ") {
|
|
if let systemMatch = systemCommands.sorted().first(where: { command in
|
|
command.hasPrefix(trimmed) && command != trimmed
|
|
}) {
|
|
return systemMatch
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getAllHistory() -> [String] {
|
|
return history
|
|
}
|
|
|
|
func clearHistory() {
|
|
history.removeAll()
|
|
saveHistory()
|
|
}
|
|
|
|
private func saveHistory() {
|
|
UserDefaults.standard.set(history, forKey: "terminal_history")
|
|
}
|
|
|
|
private func loadHistory() {
|
|
history = UserDefaults.standard.stringArray(forKey: "terminal_history") ?? []
|
|
}
|
|
}
|