From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Jean Louis Newsgroups: gmane.emacs.help Subject: Re: Auto add register when leaving buffer? Date: Sun, 10 Nov 2024 16:04:02 +0300 Message-ID: References: <874j4m2fl8.fsf@mccd.space> <87cyj4vqek.fsf@mccd.space> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="17017"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mutt/2.2.12 (2023-09-09) Cc: help-gnu-emacs@gnu.org To: Marc Coquand Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Sun Nov 10 14:05:51 2024 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1tA7dX-0004K6-7H for geh-help-gnu-emacs@m.gmane-mx.org; Sun, 10 Nov 2024 14:05:51 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tA7cz-0000Aa-Ln; Sun, 10 Nov 2024 08:05:17 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tA7cw-00007r-1e for help-gnu-emacs@gnu.org; Sun, 10 Nov 2024 08:05:14 -0500 Original-Received: from stw1.rcdrun.com ([217.170.207.13]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tA7cs-0001au-UJ for help-gnu-emacs@gnu.org; Sun, 10 Nov 2024 08:05:13 -0500 Original-Received: from localhost ([::ffff:41.75.177.128]) (AUTH: PLAIN admin, TLS: TLS1.3,256bits,ECDHE_RSA_AES_256_GCM_SHA384) by stw1.rcdrun.com with ESMTPSA id 0000000000020196.000000006730AF62.00085F30; Sun, 10 Nov 2024 06:04:32 -0700 Mail-Followup-To: Marc Coquand , help-gnu-emacs@gnu.org Content-Disposition: inline In-Reply-To: <87cyj4vqek.fsf@mccd.space> Received-SPF: pass client-ip=217.170.207.13; envelope-from=bugs@gnu.support; helo=stw1.rcdrun.com X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.help:148305 Archived-At: * Marc Coquand [2024-11-09 10:07]: > Hey! > > Not quite, I'm trying to set up so I am able to return to specific files > of a certain file-type. > > So what I had in mind: > > - ~C-x r j h~ :: Jump to last opened html file > - ~C-x r j c~ :: Jump to last opened css file > - ~C-x r j t~ :: Jump to last opened .test.go file > - ~C-x r j j~ :: Jump to last opened js file > - ~C-x r j p~ :: Jump to last opened .go file > > I can jump between buffers by using ~C-x b~ but that can sometimes be a > bit slow. I think the above setup should be feasible somehow, after all I > can manually set marks with ~C-x r X~ where X is the position I > want to register. > > If there is some hook akin to "On-leave buffer" or "On-reopen buffer" > for a certain file-type, it feels like this should be doable. The concept is interesting as you wish to extend computer and Emacs to help you quickly find last visited files by their type. That is how computer should be working, it should be helping people more and more, minimizing the efforts. Just skip to bottom if you wish... In my RCD Notes & Hyperscope, Dynamic Knowledge Repository for GNU Emacs, I have a ring ID 469 Date created "2021-05-19 03:23:42.953191+03" Date modified nil User created "maddox" User modified "maddox" Hyperdocument visited "Zeus and gods fighting" Description nil UUID "2f751526-2555-4c70-ab85-04b9b1e77c3b" Hyperscope Ring Type "Default" Hyperdocument Type "File" Hyperdocument Subtypes "Default" The types and subtypes are very similar to file types. I have ring for managed people and for managed hyperdocuments as elementary objects in the database. But I do not have a ring (list of files ) for files managed. I call it ring, as one can move forward and backward from document to document, but I could make it to move forward and backward by the type or subtype of the document visited in history. Finding the last is of course trivial. But it would be useful to move backwards for .css files only. It would not be foolproof as files could change their location, but still useful. I have to create new table for that: M-: (rcd-db-create-table "filesring" rcd-db) Table "public.filesring" ┌────────────────────────┬──────────────────────────┬───────────┬──────────┬─────────────────────────────────────────────────┐ │ Column │ Type │ Collation │ Nullable │ Default │ ├────────────────────────┼──────────────────────────┼───────────┼──────────┼─────────────────────────────────────────────────┤ │ filesring_id │ integer │ │ not null │ nextval('filesring_filesring_id_seq'::regclass) │ │ filesring_uuid │ uuid │ │ not null │ gen_random_uuid() │ │ filesring_datecreated │ timestamp with time zone │ │ not null │ CURRENT_TIMESTAMP │ │ filesring_datemodified │ timestamp with time zone │ │ │ │ │ filesring_usercreated │ text │ │ not null │ CURRENT_USER │ │ filesring_usermodified │ text │ │ not null │ CURRENT_USER │ │ filesring_name │ text │ │ not null │ │ │ filesring_description │ text │ │ │ │ └────────────────────────┴──────────────────────────┴───────────┴──────────┴─────────────────────────────────────────────────┘ Indexes: "filesring_pkey" PRIMARY KEY, btree (filesring_id) "filesring_filesring_uuid_key" UNIQUE CONSTRAINT, btree (filesring_uuid) I will keep the file under the `filesring_name` column. However, should I use MIME types or file extensions? That is the question. I have decided to use MIME types based on a capricious decision. $ psql # alter table filesring add filesring_mimetypes int4 references mimetypes not null default 1; ALTER TABLE Definition looks now this way: Table "public.filesring" ┌────────────────────────┬──────────────────────────┬───────────┬──────────┬─────────────────────────────────────────────────┐ │ Column │ Type │ Collation │ Nullable │ Default │ ├────────────────────────┼──────────────────────────┼───────────┼──────────┼─────────────────────────────────────────────────┤ │ filesring_id │ integer │ │ not null │ nextval('filesring_filesring_id_seq'::regclass) │ │ filesring_uuid │ uuid │ │ not null │ gen_random_uuid() │ │ filesring_datecreated │ timestamp with time zone │ │ not null │ CURRENT_TIMESTAMP │ │ filesring_datemodified │ timestamp with time zone │ │ │ │ │ filesring_usercreated │ text │ │ not null │ CURRENT_USER │ │ filesring_usermodified │ text │ │ not null │ CURRENT_USER │ │ filesring_name │ text │ │ not null │ │ │ filesring_description │ text │ │ │ │ │ filesring_mimetypes │ integer │ │ not null │ 1 │ └────────────────────────┴──────────────────────────┴───────────┴──────────┴─────────────────────────────────────────────────┘ Indexes: "filesring_pkey" PRIMARY KEY, btree (filesring_id) "filesring_filesring_uuid_key" UNIQUE CONSTRAINT, btree (filesring_uuid) Foreign-key constraints: "filesring_filesring_mimetypes_fkey" FOREIGN KEY (filesring_mimetypes) REFERENCES mimetypes(mimetypes_id) filesring_name is for full path filesring_mimetypes is for MIME types I could add file extension, but I leave that to reader to think about it. Function `rcd-mime-type-add-for-buffer-file' would add the MIME type for `buffer-file-name' to database. I think this is necessary. (defun rcd-mime-type-add-for-buffer-file () "Add the specified MIME type to the database for the buffer file." (interactive) (when (and buffer-file-name (file-exists-p buffer-file-name)) (rcd-mime-type-add buffer-file-name))) (defun rcd-mime-type-add (file) "Add the specified MIME type to the database for the given FILE." (let ((mime-type (rcd-mime-type file t))) (unless (rcd-sql-first "SELECT mimetypes_id FROM mimetypes WHERE mimetypes_mimetype = $1" rcd-db mime-type) (rcd-message "Adding MIME type `%s'" mime-type) (rcd-sql "INSERT INTO mimetypes (mimetypes_name, mimetypes_mimetype) VALUES ($1, $2) RETURNING mimetypes_id" rcd-db mime-type mime-type)) mime-type)) (defun rcd-files-ring-update (file mime-type) "Update table `filesring' " (let ((last-ring (rcd-files-ring-last))) (unless (and (car last-ring) (string= (cadr last-ring) file)) (let* ((mime-type (rcd-sql-first "SELECT mimetypes_id FROM mimetypes WHERE mimetypes_mimetype = $1" rcd-db mime-type)) (ring-id (rcd-sql-first "INSERT INTO filesring (filesring_name, filesring_mimetypes) VALUES ($1, $2) RETURNING filesring_id" rcd-db file mime-type))) (setq rcd-files-ring-current (or ring-id (car last-ring))))))) (defun rcd-files-ring-last () "Return last file ID, path and MIME type from `filesring' table if such exist." (rcd-sql-list-first "SELECT filesring_id, filesring_name, filesring_mimetypes FROM filesring WHERE filesring_usercreated = current_user ORDER BY filesring_id DESC LIMIT 1" rcd-db)) (defvar rcd-files-ring-current (rcd-sql-first "SELECT filesring_id FROM filesring WHERE filesring_usercreated = current_user ORDER BY filesring_id DESC LIMIT 1" rcd-db) "Files current ring item.") (defun rcd-files-ring-save () "Save file to `filesring'." (interactive) (rcd-files-ring-update buffer-file-name (rcd-mime-type-add buffer-file-name)) (rcd-message "Files ring updated for `%s'" buffer-file-name)) This looks like too much, but I just wrote it at the last minute. I think you might be wondering why I am writing all this; it is to help me stay focused. Now when I do: M-x rcd-files-ring-save What happens is that I get an entry in the database: ID 1 UUID "374f3797-9d02-46fb-90d7-e6339186f3f8" Date created "2024-11-10 14:14:10.249644+03" Date modified nil User created "maddox" User modified "maddox" File name "/home/data1/protected/Programming/emacs-lisp/hyperscope.el" Description nil MIME Type "text/x-emacs-lisp" So the file with that path and MIME type has been remembered. Let me open some other file, like "/home/data1/protected/public_html/Presentation/reveal.js-3.8.0/css/reveal.css" I run again: M-x rcd-files-ring-save and I get notification: 2024-11-10-14:24:45 Files ring updated for ‘/home/data1/protected/public_html/Presentation/reveal.js-3.8.0/css/reveal.css’ and entry in the database: ID 2 UUID "bb85afba-e17d-42ea-8196-3baafa0b4dbf" Date created "2024-11-10 14:24:45.274346+03" Date modified nil User created "maddox" User modified "maddox" File name "/home/data1/protected/public_html/Presentation/reveal.js-3.8.0/css/reveal.css" Description nil MIME Type "text/css" So it works well to add the file. If I try adding it again, it will not make duplicates if the last file was the same. So let me now add the function `rcd-files-ring-save' to `after-save-hook': (add-hook 'after-save-hook #'rcd-files-ring-save) Now each file I save, I can see: 2024-11-10-14:30:03 Files ring updated for ‘/tmp/2024-11-10-14:25:51-RCD TEMPORARY BUFFER.txt’ and I can see the list of files saved in the database: 1 /home/data1/protected/Programming/emacs-lisp/hyperscope.el 2 /home/data1/protected/public_html/Presentation/reveal.js-3.8.0/css/reveal.css 3 /home/data1/protected/Programming/emacs-lisp/hyperscope.el 4 /tmp/2024-11-10-14:25:51-RCD TEMPORARY BUFFER.txt Hmm so then I made some more functions: (defun rcd-files-ring-last-by-mime-type-id (mime-type-id) "Return last file ID by MIME type ID from `filesring' table if such exist." (let ((id (rcd-sql-list-first "SELECT filesring_id, filesring_name FROM filesring WHERE filesring_usercreated = current_user AND filesring_mimetypes = $1 ORDER BY filesring_id DESC LIMIT 1" rcd-db mime-type-id))) (when id (cadr id)))) This way I can fetch the last file by MIME-TYPE-ID as ID is integer in the database. Then one function to select MIME type and find last file: (defun rcd-files-ring-last-by-mime-type (&optional mime-type) (interactive) (let ((mime-type-id (or mime-type (rcd-db-combo-selection "mimetypes" "Select MIME type: ")))) (when mime-type-id (let ((last-file (rcd-files-ring-last-by-mime-type-id mime-type-id))) (when last-file (find-file last-file)))))) Now I can run: M-x rcd-files-ring-last-by-mime-type and quickly jump to the file based on selected MIME type. So I can say thanks for idea, I have implemented files ring, with some errors to be corrected. It is basically similar like recent file functions in Emacs Lisp, just that it is remembering it in the database and uses MIME types. I would not like making special keys or registers hard coded, so I rather prefer that key is unique to user like "c s" and that user can simply enter key combination. But that other time... ID 18 Name "text/css" Mime Type "text/css" Description nil UUID "6899f166-0b97-47bf-8d3f-78fd0dd9ebd7" Date created "2024-11-10 14:24:45.264953+03" Date modified "2024-11-10 14:59:07.576998+03" User created "maddox" User modified "maddox" Key "c s" Make it simpler: (defun my-last-css () (interactive) (rcd-files-ring-last-by-mime-type 18)) (defvar rcd-files-ring-map) (define-prefix-command 'rcd-files-ring-map) (keymap-global-set "C-c f" 'rcd-files-ring-map) (keymap-set rcd-files-ring-map "c" #'my-last-css) Now when I press `C-c f c' I get the last CSS file from the database. https://Presentation/reveal.js-3.8.0/css/reveal.css Summary: - I would be saving file name and the extension or MIME type, I prefer here MIME type as detected by system; - Somewhere I have to save those files being edited, dynamically, so in my case it goes into the database, in your case, that should be some persistent file maybe - I should be have functions to quickly find the file by the MIME type or by extension, and to find the last file - Then I assign functions to specific keys to find those last files - moving back and forth in the ring is useful; - moving back and forth by MIME type or by extension is useful With the database there are other features: - each networked user can be editing file, and files will be saved in the database for the specific user - it is possible to see which files were saved, which time in the past, useful; How to save it to file instead to database? I do not know standard ways, but I know these functions could help: (defun string-to-file-force (string file) "Prints string into file, matters not if file exists. Returns FILE as file name." (with-temp-file file (insert string)) file) (defun data-to-file (data file) "PRIN1 Emacs Lisp DATA to FILE" (string-to-file-force (prin1-to-string data) file)) (defun data-from-file (file) "Reads and returns Emacs Lisp data from FILE" (condition-case nil (car (read-from-string (file-to-string file))) (error nil))) Let us say I have list: (setq my-list '(("/file1.txt" "/file2.txt"))) my-list ➜ (("/file1.txt" "/file2.txt")) (car my-list) ➜ ("/file1.txt" "/file2.txt") (caar my-list) ➜ "/file1.txt" This list I can save to file: (data-to-file my-list "~/my-saved-data") Now I can nullify the list: (setq my-list nil) ➜ nil And try to load it again: (setq my-list (data-from-file "~/my-saved-data")) ➜ (("/file1.txt" "/file2.txt")) my-list ➜ (("/file1.txt" "/file2.txt")) (car my-list) ➜ ("/file1.txt" "/file2.txt") (caar my-list) ➜ "/file1.txt" That means I could be saving files and MIME types to files by using those principles. But that appears more complex. You could then use different files for some different file extensions or MIME types to quickly find the last file edited, or you could simply save the last file edited that way to different type or different file. I am using this function often: (defun call-process-to-string (program &optional infile display &rest args) (with-temp-buffer (apply #'call-process program infile t display args) (buffer-string))) Now there is this function that can save the file to "SAVE-FILE" which is based on MIME type: (defun rcd-file-save-last () "Save file by MIME type to $HOME." (interactive) (when buffer-file-name (let* ((mime-type (string-trim (call-process-to-string "mimetype" nil nil "-b" buffer-file-name))) (save-file (concat (file-name-as-directory (getenv "HOME")) (string-replace "/" "-" mime-type)))) (string-to-file-force buffer-file-name save-file) (message "Saved %s" save-file)))) Now all we need is the function to return the saved file by using MIME type: (defun rcd-file-open-last (&optional mime-type) (interactive) (let* ((options '("text/csv" "text/plain" "text/x-org" "text/x-emacs-lisp")) (mime-type (or mime-type (completing-read "MIME type: " options)))) (when mime-type (let ((save-file (concat (file-name-as-directory (getenv "HOME")) (string-replace "/" "-" mime-type)))) (when (file-exists-p save-file) (let ((file (file-to-string save-file))) (find-file file))))))) The above function works, and I can now open last file with: (rcd-file-open-last "text/plain") Now I can (add-hook 'after-save-hook #'rcd-file-save-last) And on each saving of the file, I get this: -rw-r--r-- 1 33 Nov 10 15:59 text-plain -rw-r--r-- 1 67 Nov 10 15:50 text-x-emacs-lisp Now I can do this: (defun my-last-plain () (interactive) (rcd-file-open-last "text/plain")) (defvar rcd-files-ring-map) (define-prefix-command 'rcd-files-ring-map) (keymap-global-set "C-c f" 'rcd-files-ring-map) (keymap-set rcd-files-ring-map "p" #'my-last-plain) And the "text/plain" will open on C-c f p key combination. It requires more customization, especially where to save such files and how to invoke them. It is not auto-adding registers though. You would need to ensure some kind of uniqueness for each MIME type for that case. -- Jean Louis