From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Joakim Jalap Newsgroups: gmane.emacs.devel Subject: Re: Imenu for cobol-mode Date: Tue, 07 Mar 2017 21:18:54 +0100 Message-ID: <87h934kfgx.fsf@fastmail.com> References: <87a892kym7.fsf@fastmail.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1488917960 11808 195.159.176.226 (7 Mar 2017 20:19:20 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 7 Mar 2017 20:19:20 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.50 (berkeley-unix) Cc: emacs-devel@gnu.org To: Edward Hart Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Mar 07 21:19:16 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 1clLZe-0001n4-Cg for ged-emacs-devel@m.gmane.org; Tue, 07 Mar 2017 21:19:10 +0100 Original-Received: from localhost ([::1]:52483 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1clLZi-0003HF-PR for ged-emacs-devel@m.gmane.org; Tue, 07 Mar 2017 15:19:14 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:42668) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1clLZX-0003Gu-AE for emacs-devel@gnu.org; Tue, 07 Mar 2017 15:19:04 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1clLZS-0004W6-BF for emacs-devel@gnu.org; Tue, 07 Mar 2017 15:19:03 -0500 Original-Received: from out1-smtp.messagingengine.com ([66.111.4.25]:43864) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1clLZS-0004VO-3U for emacs-devel@gnu.org; Tue, 07 Mar 2017 15:18:58 -0500 Original-Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id C4242208DF; Tue, 7 Mar 2017 15:18:56 -0500 (EST) Original-Received: from frontend1 ([10.202.2.160]) by compute4.internal (MEProxy); Tue, 07 Mar 2017 15:18:56 -0500 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=fastmail.com; h=cc :content-type:date:from:in-reply-to:message-id:mime-version :references:subject:to:x-me-sender:x-me-sender:x-sasl-enc :x-sasl-enc; s=mesmtp; bh=IQi66mfzFb1kgGGX0GjITORcgcM=; b=OZl730 IBIH45opsUUjAO2I20fF7sAl89LNxWKv+8MfN+IoO0zloOY+NavelMt+Xs0wTfHA vghKHJfwPnos1bfImPx2iUd2FG7UMtqOGoZ3lll2QdBX2AntA0wpJd/pid2GQmj8 gAHZVz6m4Uw5eBv0Qxc+EkldJobwtRjz05Jmo= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-sender :x-me-sender:x-sasl-enc:x-sasl-enc; s=smtpout; bh=IQi66mfzFb1kgG GX0GjITORcgcM=; b=Pri7xYJqJ1F3z6cfiPAfndacSIgM50UoPXWxeaOGbGfrH7 kUJhqR93DmPNfZ3rUN2RASqGbWw48uFnz6ndrOmmaxnb/gb8VTz7YYTs4UD8rXr+ JFuOanNksv8fPJyuUHJr3wBEymJFTcqEOz+0FMijz8GSmwE5Dn5aEm3wGinms= X-ME-Sender: X-Sasl-enc: FNkVUrqVA3rLLqAeNOowwUlOwqHigfEjtq5jTr/zkYEL 1488917936 Original-Received: from genserv (unknown [5.150.202.248]) by mail.messagingengine.com (Postfix) with ESMTPA id 047127E442; Tue, 7 Mar 2017 15:18:55 -0500 (EST) In-Reply-To: (Edward Hart's message of "Sun, 5 Mar 2017 20:13:47 +0000") X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.111.4.25 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:212821 Archived-At: --=-=-= Content-Type: text/plain Edward Hart writes: > Hi Joakim, > > I've only found one bug in the patch which involves function > definitions and program definitions in the same file. If I create an > index for such a file, the index entries for the first > program/function are not listed under the program/function's name but > just "File section", "Working-storage section", etc. The entries for > the following programs/functions are listed under their names, as > expected. I have now changed it so that if there is only one subprogram the menu items are the full "WORKING-STORAGE SECTION", "FILE SECTION" etc, but if there is more than one every subprogram's items are listed under the respective subprogram name. > I have a few suggestions for the code itself: > > * By convention, COBOL keywords are written in uppercase and I think > the regexps should be changed to reflect that. Done. > * The code assumes all programs have IDENTIFICATION DIVISION headers, > however the header is optional. Match against PROGRAM/FUNCTION-ID > instead using cobol--function-id-name-re instead (which is equivalent > to cobol--imenu-program-name). Done. > * The "^[ \t]*\\([[:digit:]]\\{1,2\\}\\|[fsr]d\\)[ \t]+\\(\\w+\\)" > regexp can be replaced with cobol--generic-declaration-re. And done :) I discovered another thing I had overlooked: There need not be paragraphs (or sections or whatever they're called) in the procedure division. Some programs just have the code directly there. So now the procedure division itself gets an entry under "PROCEDURE DIVISION" or "$subprogram_name PD" with the name of the subprogram/function. WDYT? > Two very useful features I'd like to suggest would be peeking at > (displaying the line a data item is defined on in a temporary buffer > would be good enough) and jumping to data definitions. Adding key > bindings for them would be an added bonus. I took the liberty of hacking up something :) That's the last three functions in the file. It's very ugly and I did it mostly to see if it could work, and well, it seems to work a little at least. -- Jocke --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=cobol-mode-imenu-v2.el Content-Transfer-Encoding: quoted-printable Content-Description: version 2 (defconst cobol--imenu-datadiv-sections (cobol--with-opt-whitespace-line "\\(FILE\\|WORKING-STORAGE\\|LOCAL-STORAGE\\|LINKAGE\\|REPORT\\|SCREEN\\= )" "[ \t]+SECTION") "Regexp which matches a section in the data division.") (defconst cobol--imenu-procdiv-ignore-re (cobol--with-opt-whitespace-line (regexp-opt (append cobol-scope-terminators '("EXIT" "GOBACK")))) ;; (cobol--with-opt-whitespace-line "\\(end-.*\\|exit\\.\\|goback \\)") "Things that match `cobol--procedure-re' which we don't want to add to the index.") (defun cobol--imenu-section-abbreviation (section) "Returns an abbreviation of the section name SECTION. This is to not make the menu entries too wide when there are subprograms wh= ose name is prepended to the section." (cond ((string=3D section "FILE") " FS") ((string=3D section "WORKING-STORAGE") " WSS") ((string=3D section "LOCAL-STORAGE") " LSS") ((string=3D section "LINKAGE") " LS") ((string=3D section "REPORT") " RS") ((string=3D section "SCREEN") " SS"))) (defun cobol--imenu-create-datadiv-index () "Create an Imenu sublist item for a section in the data division. Leaves point at the start of the next section heading in the data division, or at the procedure division heading if there were no more. Or eob if there is neither." (let ((idx-elms nil)) ;; We stop when we get to the next datadiv section, or when we get to t= he ;; procedure division, or when we hit eob (while (and (not (looking-at-p cobol--imenu-datadiv-sections)) (not (looking-at-p cobol--procedure-division-re)) (not (eobp))) (forward-line 1) ;; When we're looking at a data item declaration (when (looking-at cobol--generic-declaration-re) ;; Record the level number and name (let ((level (match-string-no-properties 1)) (name (match-string-no-properties 2))) ;; If it is not just a filler, put it in the list (when (not (string=3D (downcase name) "filler")) (push (cons (concat level " " name) (match-beginning 2)) idx-elms))))) ;; Reverse the list so it is in the same order as in the buffer (nreverse idx-elms))) (defun cobol--imenu-create-procdiv-index (program-name) "Create imenu index for the procedure division." ;; Search for the procedure division start (re-search-forward cobol--procedure-division-re nil t) ;; Add the procedure division itself to the list (let ((idx-elms (list (cons program-name (match-beginning 0))))) ;; While we're not at the end of buffer or at the end of the next ;; subprogram or function (while (and (not (eobp)) (not (looking-at-p cobol--function-end-marker-re))) (forward-line 1) (when (and (looking-at cobol--procedure-re) (not (looking-at-p cobol--imenu-procdiv-ignore-re))) (push (cons (match-string-no-properties 1) (match-beginning 1)) idx-elms))) (nreverse idx-elms))) (defun cobol--imenu-retrofit-subprogram-name (name index) "" (let ((current (car index)) (newindex nil)) (while index (let ((current-name (car current))) (cond ((string-match cobol--imenu-datadiv-sections current-name) (let ((section-name (match-string-no-properties 1 current-na= me))) (push (cons (concat name (cobol--imenu-section-abbreviation section-name)) (cdr current)) newindex))) ((string-match-p cobol--procedure-division-re current-name) (push (cons (concat name " PD") (cdr current)) newindex))) (setq index (cdr index)) (setq current (car index))) ) (nreverse newindex))) =20=20=20=20=20=20=20=20=20=20=20=20 (defun cobol--imenu-create-index () "Create an Imenu index for the current buffer. If there are more subprograms or functions in the buffer, there will be a submenu listing all the subprograms/functions." (goto-char (point-min)) (let ((case-fold-search t) (index nil) ;The imenu index (subprograms nil)) ;List of subprograms ;; While there are more subprograms/functions ;; (while (re-search-forward cobol--imenu-identification-division-re ni= l t) ;; (re-search-forward cobol--imenu-program-name nil t) (while (re-search-forward cobol--function-id-name-re nil t) (let ((subprogram-name (match-string-no-properties 1)) (subprogram-pos (match-beginning 1))) ;; If we get here and `subprograms' has one element, that means the= re ;; are at least two subprograms in the file. Retrofit the last ;; subprogram name to the existing menus. (when (=3D (length subprograms) 1) (setq index (cobol--imenu-retrofit-subprogram-name (caar subprograms) index))) ;; While we're not at the start of the procedure division, search ;; forward for a section in the data division (while (and (not (looking-at-p cobol--procedure-division-re)) (re-search-forward cobol--imenu-datadiv-sections nil t)) (let ((section (match-string-no-properties 1)) (subidx (cobol--imenu-create-datadiv-index))) (when subidx (push (cons (if subprograms (concat subprogram-name (cobol--imenu-section-abbreviation section)) (concat (upcase section) " SECTION")) subidx) index)))) ;; We're done indexing the data declarations, now do the procedure ;; division (let ((procdiv-idx (cobol--imenu-create-procdiv-index subprogram-na= me))) (when procdiv-idx (push (cons (if subprograms (concat subprogram-name " PD") "PROCEDURE DIVISION") procdiv-idx) index))) (push (cons subprogram-name subprogram-pos) subprograms))) ;; If there were multiple programs defined in this file make a submenu = with ;; their names (when (> (length subprograms) 1) (push (cons "Subprograms" (nreverse subprograms)) index)) (nreverse index))) (defun cobol--find-definition (item) "Find the position of the definition of ITEM by searching for it in `imenu-index-alist'. This is very inefficient!" (require 'seq) (when (not imenu--index-alist) (user-error "Bummer")) (let* ((res nil) (index imenu--index-alist) (sublist (cdar index))) (while (and (not res) sublist) (setq res (seq-find (lambda (elm) (let* ((entrystr (car elm)) (identifier (if (string-match cobol--generic-declaration-re entr= ystr) (match-string-no-properties 2 entrystr) entrystr))) (string=3D item identifier))) sublist)) (setq index (cdr index)) (setq sublist (cdar index))) (when res (cdr res)))) =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20 (defun cobol-goto-definition () "Goto the definition of the identifier under point." (interactive) (let* ((identifier (symbol-at-point)) (pos (cobol--find-definition identifier))) (when pos (push-mark) (goto-char pos)))) (defun cobol-peek-definition () "Peek at the definition of the identifier under point." (interactive) (let* ((identifier (symbol-at-point)) (pos (cobol--find-definition identifier))) (when pos (message "%s" (save-excursion (goto-char pos) (cobol-back-to-indentation) (buffer-substring (point) (line-end-position))))))) --=-=-=--