Warning, /sdk/kde-dev-scripts/kde-emacs/sourcepair.el is written in an unsupported language. File is not indexed.

0001 ;; sourcepair.el --- Load the corresponding C/C++ header or source file for the current buffer.
0002 ;;
0003 ;; Copyright (C) 2007 Mohamed Hendawi
0004 ;;
0005 ;; Emacs Lisp Archive Entry
0006 ;; Filename: sourcepair.el
0007 ;; Version: 1.01
0008 ;; Keywords:   c languages oop
0009 ;; Author: Mohamed Hendawi <moedev *AT* hendawi *DOT* com>
0010 ;; Description: Load the corresponding C/C++ header or source file for the current buffer.
0011 ;; URL: http://www.hendawi.com/emacs/sourcepair.el
0012 ;; Compatibility: Emacs20, Emacs21
0013 ;;
0014 ;; $Id: sourcepair.el,v 1.15 2007/10/22 11:41:24 moe Exp $
0015 ;;
0016 ;; This program is free software; you can redistribute it and/or
0017 ;; modify it under the terms of the GNU General Public License as
0018 ;; published by the Free Software Foundation; either version 2 of
0019 ;; the License, or (at your option) any later version.
0020 ;; 
0021 ;; This program is distributed in the hope that it will be useful,
0022 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
0023 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0024 ;; GNU General Public License for more details.
0025 ;; 
0026 ;; You should have received a copy of the GNU General Public
0027 ;; License along with this program; if not, write to the Free
0028 ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
0029 ;; MA 02110-1301 USA
0030 ;;
0031 ;; This code is inspired by a similar function written by Abiyu Diro.
0032 ;; Thanks to Jesper Pedersen for idea and initial implementation of
0033 ;; support for private header files.
0034 ;;
0035 ;;; Commentary:
0036 ;;
0037 ;; This Emacs lisp file provides the function "sourcepair-load" which will load
0038 ;; the corresponding header or source file for the current buffer.  For example,
0039 ;; if you are looking at the file FooParser.cpp and enter M-x sourcepair-load 
0040 ;; (or whatever keybinding you've set), the file FooParser.h will be loaded.  
0041 ;; It also works the other way as well.  To use it put this file somewhere 
0042 ;; in your lisp library path and then add something like this to your .emacs 
0043 ;; file:
0044 ;;
0045 ;; (load-file "sourcepair.el")
0046 ;;
0047 ;; KEYBINDINGS:
0048 ;;
0049 ;; You should set a keybinding to use this function easily.  For example, I add
0050 ;; the following to my .emacs file:
0051 ;;
0052 ;; (define-key global-map "\C-xz" 'sourcepair-load)
0053 ;;
0054 ;;
0055 ;; GLOBAL VARIABLES:
0056 ;;
0057 ;; There are six global variables that can be used to adjust how the function
0058 ;; works:
0059 ;;
0060 ;; sourcepair-source-extensions :
0061 ;;
0062 ;;    A list containing the recognized extensions for source files.  By default
0063 ;;    this is set to ( ".cpp" ".cxx" ".cc" ".c" ).  For example: with the default 
0064 ;;    setting if you are looking at "foo.h", the function will look for 
0065 ;;    "foo.cpp", "foo.cxx", "foo.cc" or "foo.c" in that order in the 
0066 ;;    directories specified by sourcepair-source-path.
0067 ;;
0068 ;; sourcepair-header-extensions :
0069 ;;
0070 ;;    A list containing the recognized extensions for header files.  By default
0071 ;;    this is set to ( ".h" ".hpp" ".hh" ).  For example: with the default 
0072 ;;    setting if you are looking at "foo.cpp", the function will look for 
0073 ;;    "foo.h", "foo.hpp" or "foo.hh" in that order in the directories specified
0074 ;;    by sourcepair-header-path.
0075 ;;    
0076 ;; sourcepair-source-path :
0077 ;;
0078 ;;    A list containing the path's to search for source files.  By default this
0079 ;;    is set to ( "." ) which means source files will only be searched for in
0080 ;;    the current directory.  Paths that end in "/*" will be searched 
0081 ;;    recursively.  For example, if you specified sourcepair-source-path as
0082 ;;    ( "." "../*" ) the function will look for source files first in the 
0083 ;;    current directory, and then in the parent directory, and then in any 
0084 ;;    subdirectories of the parent directory.
0085 ;;
0086 ;; sourcepair-header-path :
0087 ;;
0088 ;;    Similar to sourcepair-source-path except for header files.
0089 ;;
0090 ;; sourcepair-recurse-ignore :
0091 ;;
0092 ;;    A list containing directories to ignore when recursively searching
0093 ;;    subdirectories for header or source files.  By default this is set
0094 ;;    to ( "CVS" )
0095 ;;
0096 ;; sourcepair-private-header-suffixes :
0097 ;;
0098 ;;    A list containing suffixes that will be ignored when searching 
0099 ;;    for the corresponding source file for a given header file.  This
0100 ;;    allows supporting "private header files".   For example, Foo.cpp 
0101 ;;    has a public interface in "Foo.h" and a private interface in 
0102 ;;    "Foo_p.h".  By default this is set to ( "_p" "_impl" ).
0103 ;;
0104 ;; For example, in my .emacs file I have the following:
0105 ;;
0106 ;; (setq sourcepair-source-path    '( "." "../*" ))
0107 ;; (setq sourcepair-header-path    '( "." "include" "../include" "../*"))
0108 ;; (setq sourcepair-recurse-ignore '( "CVS" "Obj" "Debug" "Release" ))
0109 
0110 ;;; Code:
0111 
0112 (defcustom sourcepair-source-extensions '( ".cpp" ".CPP" ".Cpp" ".cxx" ".CXX" ".cc" ".CC" ".c" ".C" ".c++" ".C++")
0113   "*List of recognized extensions for source files.
0114 
0115 This variable is used by `sourcepair-load'.  The value should be a list
0116 containing the recognized extensions for source files.  For example: if the
0117 value is ( \".cpp\" \".cxx\" \".cc\" \".C\" \".c\" ), and you are looking at
0118 \"foo.h\", `sourcepair-load' will look for \"foo.cpp\", \"foo.cxx\",
0119 \"foo.cc\" or \"foo.c\" in that order in the directories specified by
0120 `sourcepair-source-path'."
0121 :type '(repeat string)
0122 :group 'sourcepair)
0123 
0124 (defcustom sourcepair-header-extensions '( ".h" ".H" ".hpp" ".HPP" ".Hpp" ".hh" ".HH" ".hxx" ".HXX")
0125   "*List of recognized extensions for header files.
0126 
0127 This variable is used by `sourcepair-load'.  The value should be a list
0128 containing the recognized extensions for header files.  For example: if the
0129 value is (\".h\" \".hpp\" \".hh\" ), and you are looking at \"foo.cpp\",
0130 `sourcepair-load' will look for \"foo.h\", \"foo.hpp\" or \"foo.hh\" in that
0131 order in the directories specified by `sourcepair-header-path'."
0132 :type '(repeat string)
0133 :group 'sourcepair)
0134 
0135 (defcustom sourcepair-private-header-suffixes '( "_p" "_impl" )
0136   "*List of recognized suffixes for 'private' header files.
0137 
0138 This variable is used by `sourcepair-load' to help support 'private header 
0139 files'.  The value should be a list containing recognized suffixes that will
0140 be ignored when searching for the corresponding source file for a given 
0141 header file.  For example, Foo.cpp is an implementation of what is in
0142 Foo_p.h.  If you set this variable to include (\"_p\") and you are looking 
0143 at \"Foo_p.h\" or \"Foo.h\", `sourcepair-load' will load the file \"Foo.cpp\".
0144 
0145 "
0146 :type '(repeat-string)
0147 :group 'sourcepair)
0148 
0149 (defcustom sourcepair-source-path       '( "." )
0150   "*List of directories to search for corresponding source file.
0151 
0152 This variable is used by `sourcepair-load'.  The value should be a list
0153 containing the directories to search for source files.  By default this is set
0154 to ( \".\" ) which means source files will only be searched for in the current
0155 directory.  Paths that end in \"/*\" will be searched recursively.  For
0156 example, if you specified `sourcepair-source-path' as ( \".\" \"../*\" )
0157 `sourcepair-load' will look for source files first in the current directory,
0158 and then in the parent directory, and then in any subdirectories of the parent
0159 directory."
0160 :type '(repeat string)
0161 :group 'sourcepair)
0162 
0163 (defcustom sourcepair-header-path       '( "." )
0164   "*List of directories to search for corresponding header file.
0165 
0166 This is similar to `sourcepair-source-path' except for header files.  See the
0167 documentation for `sourcepair-source-path' for more info."
0168 :type '(repeat string)
0169 :group 'sourcepair)
0170 
0171 (defcustom sourcepair-recurse-ignore    '( "CVS" )
0172   "*List of directories to ignore when recursively searching subdirectories.
0173 
0174 This variable is used by `sourcepair-load'.  The value should be a list
0175 containing the names of directories to ignore when `sourcepair-load' is
0176 recursively searching subdirectories for header or source files.  By default
0177 this is set to ( \"CVS\" )"
0178 :type '(repeat string)
0179 :group 'sourcepair)
0180 
0181 (defun sourcepair-header-file-p (filename)
0182   "Return t if argument is a C/C++ header file, nil otherwise
0183 
0184 This function returns t if the filename specified is a C/C++ header 
0185 file, or nil otherwise.  Header files are identified by extension via 
0186 the variable `sourcepair-header-extensions'."
0187 
0188   (let* ((extension (concat (member ?. (append filename nil))))
0189                  (basename (substring filename 0 (- 0 (length extension)))))
0190         (if (member extension sourcepair-header-extensions)
0191                 t
0192           nil)))
0193 
0194 
0195 (defun sourcepair-source-file-p (filename)
0196   "Return t if argument is a C/C++ source file, nil otherwise
0197 
0198 This function returns t if the filename specified is a C/C++ source file,
0199 or nil otherwise.  Source files are identified by extension via the
0200 variable `sourcepair-source-extensions'."
0201 
0202   (let* ((extension (concat (member ?. (append filename nil))))
0203                  (basename (substring filename 0 (- 0 (length extension)))))
0204         (if (member extension sourcepair-source-extensions)
0205                 t
0206           nil)))
0207 
0208 
0209 (defun sourcepair-remove-private-suffixes (basename)
0210   (car (delete 'nil (append (mapcar '(lambda (suffix) 
0211                                                                            (if (string= (substring basename (- (length basename) (length suffix))) suffix)
0212                                                                                    (substring basename 0 (- (length basename) (length suffix)))))
0213                                                                         sourcepair-private-header-suffixes)
0214                                                         (list basename)))))
0215 
0216 (defun sourcepair-analyze-filename (filename)
0217   (let* ((extension (concat (member ?. (append filename nil))))
0218                  (basename (substring filename 0 (- 0 (length extension)))))
0219         
0220         (if (member extension sourcepair-header-extensions)
0221                 (progn (setq basename (sourcepair-remove-private-suffixes basename))
0222                            (cons sourcepair-source-path (mapcar '(lambda (arg) (concat basename arg)) sourcepair-source-extensions)))
0223           (if (member extension sourcepair-source-extensions)
0224                   (cons sourcepair-header-path 
0225                                 (apply 'append 
0226                                            (mapcar '(lambda (suffix) (mapcar '(lambda (ext) (concat basename suffix ext)) sourcepair-header-extensions))
0227                                                            (append '("") sourcepair-private-header-suffixes))))))))
0228 
0229 (defun sourcepair-find-one-of (path choices recurse)
0230   (catch 'matching-filename
0231         (if (file-directory-p path)
0232                 (let ((possible-filenames choices)
0233                           (matching-filename nil)
0234                           (files-in-directory nil))
0235                   
0236                   ;; Check if there's a match in this directory
0237                   (while possible-filenames
0238                         (let ((possible-filename (expand-file-name (car possible-filenames) path)))
0239                           (if (file-exists-p possible-filename)
0240                                   (throw 'matching-filename possible-filename)
0241                                 (setq possible-filenames (cdr possible-filenames)))))
0242                   
0243                   ;; Recursively search subdirectories
0244                   (if (not (eq recurse nil))
0245                           (progn
0246                                 (setq files-in-directory (directory-files path nil "^[^\\.]"))
0247                                 (while files-in-directory
0248                                   (let ((possible-subdir (car files-in-directory)))
0249                                         (if (not (member possible-subdir sourcepair-recurse-ignore))
0250                                                 (progn
0251                                                   (setq possible-subdir (expand-file-name possible-subdir path))
0252                                                   (if (file-directory-p possible-subdir)
0253                                                           (progn 
0254                                                                 (message "Checking %s" possible-subdir)
0255                                                                 (setq matching-filename 
0256                                                                           (sourcepair-find-one-of possible-subdir choices t))
0257                                                                 (if (not (eq matching-filename nil))
0258                                                                         (throw 'matching-filename matching-filename))))))
0259                                         (setq files-in-directory (cdr files-in-directory))))))))
0260         ;; Return nil if nothing found
0261         nil))
0262 
0263 (defun sourcepair-matching-file-for-file (filename)
0264   (catch 'found-matching-file
0265     (let* ((temp (sourcepair-analyze-filename (file-name-nondirectory filename)))
0266            (search-path (car temp))
0267            (possible-filenames (cdr temp)))
0268       (if (= (length possible-filenames) 0)
0269           (message "%s is not a recognized source or header file (consider updating sourcepair-source-extensions or sourcepair-header-extensions)" (buffer-name))
0270         (progn
0271           (while search-path
0272             (let ((path-to-check (car search-path))
0273                   (matching-filename nil))
0274               (if (and (> (length path-to-check) 3)
0275                        (equal (substring path-to-check -2) "/*"))
0276                   (setq matching-filename (sourcepair-find-one-of (substring path-to-check 0 -2)
0277                                                                   possible-filenames
0278                                                                   t))
0279                 (setq matching-filename 
0280                       (sourcepair-find-one-of path-to-check possible-filenames nil)))
0281                 
0282               (if (eq matching-filename nil)
0283                   (setq search-path (cdr search-path))
0284                 (throw 'found-matching-file matching-filename))))
0285 
0286           nil)))))
0287 
0288 (defun sourcepair-load ()
0289   "Load the corresponding C/C++ header or source file for the current buffer.
0290 
0291 This function can be invoked by \\[sourcepair-load].  It will load the the
0292 corresponding header or source file for the current buffer.  For example, if
0293 you are looking at the file FooParser.cpp and press \\[sourcepair-load], the
0294 file FooParser.h will be loaded.  It also works the other way as well.
0295 
0296 There are six global variables that can be used to adjust how the function
0297 works:
0298 
0299  `sourcepair-source-extensions'
0300  `sourcepair-header-extensions'
0301  `sourcepair-source-path'
0302  `sourcepair-header-path'
0303  `sourcepair-recurse-ignore'
0304  `sourcepair-private-header-suffixes'
0305 
0306 See the documentation for these variables for more info.
0307 "
0308 
0309   (interactive)
0310 
0311   (let ((file (sourcepair-matching-file-for-file (buffer-file-name))))
0312     (if file
0313         (find-file file)
0314       (message (concat "No matching file for " (buffer-name)
0315                        " (consider updating sourcepair-source-path, sourcepair-header-path)")))))
0316 
0317 (defun sourcepair-jump-to-headerfile (prefix)
0318   "Jump to header file for class at point"
0319   (interactive "P")
0320   (save-excursion
0321     (let* ((word-at-point (if prefix
0322                               (read-from-minibuffer "Class: ")
0323                             (current-word)))
0324            (file1 (sourcepair-matching-file-for-file (concat word-at-point ".cpp" )))
0325            (file (if file1 file1 (sourcepair-matching-file-for-file (concat (downcase word-at-point) ".cpp" )))))
0326       (if file
0327           (find-file file)
0328         (message "Sorry couldn't find include file for class")))))
0329 
0330 (defun sourcepair-yank-advice ()
0331   "Advice function called after a yank.
0332 
0333 This function is called when advising the yank function.  If you are 
0334 looking at a header file and paste a method declaration that was copied 
0335 from a source file, this function will remove the class prefix (e.g.
0336 \"Foo::\"), add a semicolon at the end of the declaration and reindent the
0337 region.  If you paste something other than a method declaration this 
0338 function will just reindent the region.
0339 "
0340   (if (member major-mode '(c-mode c++-mode))
0341           (if (sourcepair-header-file-p (buffer-name))
0342                   (let* ((this-buffer-name (buffer-name))
0343                                  (extension (concat (member ?. (append this-buffer-name nil))))
0344                                  (basename (substring this-buffer-name 0 (- 0 (length extension))))
0345                                  (class-prefix (concat basename "::"))
0346                                  (begin-point  (region-beginning))
0347                                  (end-point    (region-end))
0348                                  (region-len   (- end-point begin-point)))
0349                         (save-excursion
0350                           (set-window-point nil (- (point) region-len))
0351                           (if (re-search-forward class-prefix end-point t)
0352                                   (progn
0353                                         (replace-match "" nil t)
0354                                         (set-window-point nil (- end-point (length class-prefix) 1))
0355                                         (re-search-backward "[^ ]")
0356                                         (set-window-point nil (+ (point) 1))
0357                                         (insert ";")
0358                                         (indent-region (region-beginning) (region-end) nil)
0359                                         (message "Removed class prefix when pasting"))
0360                                 (indent-region begin-point end-point nil))))
0361                 (indent-region (region-beginning) (region-end) nil))))
0362 
0363 ; This allows (require 'sourcepair)
0364 (provide 'sourcepair)
0365 
0366 ;;; sourcepair.el ends here
0367