From: Jean Louis <bugs@gnu.support>
To: Marc Coquand <marc@mccd.space>
Cc: help-gnu-emacs@gnu.org
Subject: Re: Auto add register when leaving buffer?
Date: Sun, 10 Nov 2024 16:04:02 +0300 [thread overview]
Message-ID: <ZzCvQvday9vD3KWY@lco2> (raw)
In-Reply-To: <87cyj4vqek.fsf@mccd.space>
* Marc Coquand <marc@mccd.space> [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 <SPC> 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
prev parent reply other threads:[~2024-11-10 13:04 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-05 9:29 Auto add register when leaving buffer? Marc Coquand
2024-11-05 11:43 ` Michael Heerdegen via Users list for the GNU Emacs text editor
2024-11-08 12:49 ` Jean Louis
2024-11-09 7:04 ` Marc Coquand
2024-11-10 13:04 ` Jean Louis [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZzCvQvday9vD3KWY@lco2 \
--to=bugs@gnu.support \
--cc=help-gnu-emacs@gnu.org \
--cc=marc@mccd.space \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).