From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: "Huang\, Ying" Newsgroups: gmane.emacs.devel Subject: [ELPA] New package: project-shells Date: Fri, 24 Feb 2017 20:36:47 +0800 Message-ID: <87mvdbojds.fsf@163.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: blaine.gmane.org 1487945212 18185 195.159.176.226 (24 Feb 2017 14:06:52 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 24 Feb 2017 14:06:52 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1 (gnu/linux) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Feb 24 15:06:47 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1chGWE-0004Bz-A4 for ged-emacs-devel@m.gmane.org; Fri, 24 Feb 2017 15:06:46 +0100 Original-Received: from localhost ([::1]:37584 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1chGWK-0001Ip-0u for ged-emacs-devel@m.gmane.org; Fri, 24 Feb 2017 09:06:52 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:34116) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1chF7L-0001WW-JE for emacs-devel@gnu.org; Fri, 24 Feb 2017 07:37:01 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1chF7I-0001Yd-Dl for emacs-devel@gnu.org; Fri, 24 Feb 2017 07:36:59 -0500 Original-Received: from m12-18.163.com ([220.181.12.18]:58642) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1chF7H-0001Rn-0n for emacs-devel@gnu.org; Fri, 24 Feb 2017 07:36:56 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:Subject:Date:Message-ID:MIME-Version; bh=ivzI2 89X0u2pg3lukqNb+fZA2qHmATnG2x5VqIoKrxY=; b=GJGdzYwCrf4HMXun7rbgi jCAuUGnXTach8CioowDnfmh1IeFwwOsXL6CX+0ImOZUHBcaW87dFILfXqL2XqWij 2i9U3wLpbz/7TBJXjRt6749SeeNkoBAY2wQOjRPAPAIHLd/pGSQTqUzm9lX2G82B YpXICTKu5XetJMhk+irFaQ= Original-Received: from yhuang-guixsd (unknown [222.65.4.40]) by smtp14 (Coremail) with SMTP id EsCowABHa7TfKLBYj0iMKw--.11861S2; Fri, 24 Feb 2017 20:36:49 +0800 (CST) X-CM-TRANSID: EsCowABHa7TfKLBYj0iMKw--.11861S2 X-Coremail-Antispam: 1Uf129KBjvJXoW3Wr4ktF15Zw1DtFy5CFy3Arb_yoWDXr47pa n8t39rGrWkCF18JFyDJF17W3Z3Grn2q34IkFy5G3yUX3W5Xw4xXrnxtr98WFyjyrnFg345 XrWvgrykG3W7JaDanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07bVc_-UUUUU= X-Originating-IP: [222.65.4.40] X-CM-SenderInfo: xkxd0wxb1l0wxbfd2xxwdvqiywtou0bp/1tbiLwGItlUMAWaEFAAAs+ X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 220.181.12.18 X-Mailman-Approved-At: Fri, 24 Feb 2017 09:06:41 -0500 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:212567 Archived-At: Hi, All, This is to manage multiple shell (or terminal) buffers for each project. For example, to develop for Linux kernel, I usually use one shell buffer to configure and build kernel, one shell buffer to run some git command not supported by magit, one shell buffer to run qemu for built kernel, one shell buffer to ssh into guest system to test. Different set of commands is used by the shell in each buffer, so each shell should have different command history configuration, and for some shell, I may need different setup. And I have several projects to work on. In addition to project specific shell buffers, I want some global shell buffers, so that I can use them whichever project I am working on. Project shells is an Emacs program to let my life easier via helping me to manage all these shell/terminal buffers. If you think this may be helpful for someone and there is no existing package has same functionality, could you help me to upload this into ELPA archive? The package is as follow. ;;; project-shells.el --- Manage the shell buffers of each project -*- lexical-binding: t -*- ;; Copyright (C) 2017 "Huang, Ying" ;; Author: "Huang, Ying" ;; Maintainer: "Huang, Ying" ;; URL: https://github.com/hying-caritas/project-shells ;; Version: 20170222 ;; Package-Version: 20170222 ;; Package-Type: simple ;; Keywords: project, shell, terminal ;; Package-Requires: ((pkg-info "0.4")) ;; This file is NOT part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; Manage multiple shell/terminal buffers for each project. For ;; example, to develop for Linux kernel, I usually use one shell ;; buffer to configure and build kernel, one shell buffer to run some ;; git command not supported by magit, one shell buffer to run qemu ;; for built kernel, one shell buffer to ssh into guest system to ;; test. Different set of commands is used by the shell in each ;; buffer, so each shell should have different command history ;; configuration, and for some shell, I may need different setup. And ;; I have several projects to work on. In addition to project ;; specific shell buffers, I want some global shell buffers, so that I ;; can use them whichever project I am working on. Project shells is ;; an Emacs program to let my life easier via helping me to manage all ;; these shell/terminal buffers. (require 'cl-lib) (defvar-local project-shells-project-name nil) (defvar-local project-shells-project-root nil) ;;; Customization (defgroup project-shells nil "Manage shells of projects" :group 'tools :link '(url-link :tag "Github" "https://github.com/hying-caritas/project-shells")) (defcustom project-shells-default-shell-name "sh" "The default shell buffer name" :group 'project-shells :type 'string) (defcustom project-shells-empty-project "-" "Specify the name of the empty project. This is used to create non-project specific shells." :group 'project-shells :type 'string) (defcustom project-shells-setup `((,project-shells-empty-project . (("1" . (,project-shells-default-shell-name "~/" 'shell nil))))) "Specify the setup for shells of each project. Including name, initial directory, type, function to intialize, etc." :group 'project-shells :type '(alist :key-type (string :tag "Project") :value-type (alist :tag "Project setup" :key-type (string :tag "Key") :value-type (list :tag "Shell setup" (string :tag "Name") (string :tag "Directory") (choice :tag "Type" (const term) (const shell)) (choice :tag "Function" (const nil) function))))) (defcustom project-shells-keys '("1" "2" "3" "4" "5" "6" "7" "8" "9" "0" "-" "=") "Specify keys for shells, one shell will be created for each key. Usually these key will be bound in a non-global keymap." :group 'project-shells :type '(repeat string)) (defcustom project-shells-term-keys '("-" "=") "Specify keys to create terminal. By default shell mode will be used, but for keys in project-shells-term-keys, ansi terminal mode will be used. This should be a subset of *poject-shells-keys*." :group 'project-shells :type '(repeat string)) (defcustom project-shells-project-name-func 'projectile-project-name "Specify function to get project name." :group 'project-shells :type 'function) (defcustom project-shells-project-root-func 'projectile-project-root "Specify function to get project root directory" :group 'project-shells :type 'function) (defcustom project-shells-histfile-env "HISTFILE" "Specify environment variable to set shell history file" :group 'project-shells :type 'string) (defcustom project-shells-histfile-name ".shell_history" "Specify shell history file name" :group 'project-shells :type 'string) (defcustom project-shells-term-args nil "Specify shell argument used in terminal" :group 'project-shells :type 'string) (let ((saved-shell-buffer-list nil) (last-shell-name)) (cl-defun shell-buffer-list () (setf saved-shell-buffer-list (cl-remove-if-not #'buffer-live-p saved-shell-buffer-list))) (cl-defun project-shells-switch (&optional name to-create) (interactive "bShell: ") (let* ((name (or name last-shell-name)) (buffer-list (shell-buffer-list)) (buf (when name (cl-find-if (lambda (b) (string= name (buffer-name b))) buffer-list)))) (when (and (or buf to-create) (cl-find (current-buffer) buffer-list)) (setf last-shell-name (buffer-name (current-buffer)))) (if buf (progn (select-window (display-buffer buf)) buf) (unless to-create (message "No such shell: %s" name) nil)))) (cl-defun project-shells-switch-to-last () (interactive) (let ((name (or (and last-shell-name (get-buffer last-shell-name) last-shell-name) (and (shell-buffer-list) (buffer-name (first (shell-buffer-list))))))) (if name (project-shells-switch name) (message "No more shell buffers!")))) (cl-defun project-shells-create (name dir &optional (type 'shell) func) (let ((default-directory (expand-file-name (or dir "~/")))) (cl-ecase type ('term (ansi-term "/bin/sh")) ('shell (shell))) (rename-buffer name) (push (current-buffer) saved-shell-buffer-list) (when func (funcall func))))) (cl-defun project-shells-create-switch (name dir &optional (type 'shell) func) (unless (project-shells-switch name t) (project-shells-create name dir type func))) (cl-defun project-shells-send-shell-command (cmdline) (insert cmdline) (comint-send-input)) (cl-defun project-shells-project-name () (or project-shells-project-name (funcall project-shells-project-name-func) project-shells-empty-project)) (cl-defun project-shells-project-root (proj-name) (if (string= proj-name project-shells-empty-project) "~/" (or project-shells-project-root (funcall project-shells-project-root-func)))) (cl-defun project-shells-set-histfile-env (val) (when (and project-shells-histfile-env project-shells-histfile-name) (setenv project-shells-histfile-env val))) (cl-defun project-shells-escape-sh (str) (replace-regexp-in-string "\"" "\\\\\"" (replace-regexp-in-string "\\\\" "\\\\\\\\" str))) (cl-defun project-shells-command-string (args) (mapconcat #'identity (cl-loop for arg in args collect (concat "\"" (project-shells-escape-sh arg) "\"")) " ")) (cl-defun project-shells-term-command-string () (let* ((prog (or explicit-shell-file-name (getenv "ESHELL") shell-file-name))) (concat "exec " (project-shells-command-string (cons prog project-shells-term-args)) "\n"))) (cl-defun project-shells-activate (key &optional proj proj-root) (let* ((proj (or proj (project-shells-project-name))) (proj-root (or proj-root (project-shells-project-root proj))) (proj-shells (cdr (assoc proj project-shells-setup))) (shell-info (cdr (assoc key proj-shells))) (name (or (first shell-info) project-shells-default-shell-name)) (dir (or (second shell-info) proj-root)) (type (or (third shell-info) (if (member key project-shells-term-keys) 'term 'shell))) (func (fourth shell-info)) (shell-name (format "*%s.%s.%s*" key name proj)) (session-dir (expand-file-name (format "~/.sessions/%s/%s" proj key)))) (mkdir session-dir t) (project-shells-set-histfile-env (format "%s/%s" session-dir project-shells-histfile-name)) (project-shells-create-switch shell-name dir type (lambda () (when func (funcall func session-dir)) (when (eq type 'term) (term-send-raw-string (project-shells-term-command-string))) (setf project-shells-project-name proj project-shells-project-root proj-root))) (project-shells-set-histfile-env nil))) (cl-defun project-shells-setup (map &optional setup) (when setup (setf project-shells-setup setup)) (cl-loop for key in project-shells-keys do (define-key map (kbd key) (let* ((key key)) (lambda (p) (interactive "p") (project-shells-activate key (and (/= p 1) project-shells-empty-project))))))) (provide 'project-shells)