because parsing the text file is tedious store in elisp format and have a special mode to read it and show it in a buffer.
persistent file is project based or global
must have something like project based management mechanism to share
such as tide.
when a buffer with icl mode on started up it will locate its project root which resolves to global or project root. The project root is now a unique key to indentify the comment store
there is a global comment store which is a map of project root -> project comment store
project comment store:
- comments: (file (line . comment)…)
- count: number of active buffers reading this store, when is is down to zero comments will be set to nil
- hook: this hook runs on store changed
in project A, buffer a starts up and gets its project unique key as A, From the global comment stores it gets it project comment store. It is nil and the count is now set to 0. With the count value it knows that it is the first one to access this store. It updates the count to one and initializes comments value for project. Then it reads the comments from the store and show the comments in buffers.
Next a buffer b in project A starts up, and follows the same procedure but the count is already greater than 0 so it means the project comment store is already set up to go. So buffer b will just use the store and increase the count.
Some time later buffer b closes, it will decrease the count. It check the count is still greater than 0
And finally buffer a is about to close. After decreasing the count to 0, that means it is the last consumer of the store so it set the comment variable to nil. Persist the data to a .ilc file
No memory leak this way.
how buffer updates data
(get-text-property (point) ‘occur-target)
use propertize to make string
cask install cask exec ert-runnerhash table project: file -> comments { line: comment }
((#<overlay from 8503 to 8503 in ipa.el> .
#("need to forward-char here" 0 25
(fontified nil)))
(#<overlay from 22765 to 22765 in ipa.el> .
#("use project
multiple line really" 0 11
(fontified nil)
12 32
(fontified nil)))
(#<overlay from 23192 to 23192 in ipa.el> .
#("take care of project option" 0 27
(fontified nil))))
https://github.com/sigma/pcache
- project-id: md5
@startjson
{
"virtual-comment-store" : {
"default" : {},
"projects" : {
"project-id" : {
"count" : "Number",
"files" : {
"file-id" : {
"comments" : [
{
"comment" : "String",
"point" : "Number",
"target" : "String"
},
{
"comment" : "String",
"point" : "Number",
"target" : "String"
}
],
"file-name" : "String"
},
"file-id-2" : []
}
},
"project-id-2" : {}
}
}
}
@endjson
(overlay-put (make-overlay (point) (point) (current-buffer)) ‘before-string “crap”)
(overlay-put (make-overlay (point) (point) (current-buffer)) ‘before-string “crap”)
if only data structure is kept i.e line number and string then when overlay is moved because of buffer change then we lose track of overlay
overlay should stick to the line it belongs to, eg symbol-overlay
in buffer
we don’t manage the ov list anymore sorting it keep it in order is a headache just grab all the overlays in the buffer which has the tag then this is it.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Finding-Overlays.html
for next and previous next-overlay-change pos previous-overlay-change pos
how to extract and make/separate comment from indentation the real string stored in ‘virtual-comment tag ‘before-string is to store the presentational text
there is a hook but it won’t get triggered on some occasions so we won’t handle it. instead we provide functions to repair, copy and paste comment
yank, paste
read-from-string is a built-in function in ‘C source code’.
(read-from-string STRING &optional START END)
Read one Lisp expression which is represented as text by STRING. Returns a cons: (OBJECT-READ . FINAL-STRING-INDEX). FINAL-STRING-INDEX is an integer giving the position of the next remaining character in STRING. START and END optionally delimit a substring of STRING from which to read; they default to 0 and (length STRING) respectively. Negative values are counted from the end of STRING.
it’s a experiment
(defun virtual-comment-buffer-overlays--add (ov my-list)
"MY-LIST has at least one element and its head is smaller than OV."
(let ((start (overlay-start ov))
(head (car my-list))
(tail (cdr my-list)))
(if (or (not tail)
(<= start (overlay-start (car tail))))
(setcdr my-list (cons ov tail))
(virtual-comment-buffer-overlays--add ov tail))))
(defun virtual-comment-buffer-overlays-add (ov)
"Add OV to `virtual-comment-buffer-overlays' in order."
(if (or (not virtual-comment-buffer-overlays)
(< (overlay-start ov) (overlay-start (car virtual-comment-buffer-overlays))))
(push ov virtual-comment-buffer-overlays)
(virtual-comment-buffer-overlays--add ov virtual-comment-buffer-overlays)))
tbd run-with-idle-timer https://www.gnu.org/software/emacs/manual/html_node/elisp/Idle-Timers.html
virtual-comment–update-async functions that needs to call this yank make paste realign should we make it as a hook
- show list of commnents of current buffer
- show list of files with comments of projects
- jump to place
based on outline mode or occur mode how about org mode, outline mode lacks some commands
org mode is the best but unable to bind RET key when evil is on so use outline mode instead
from helm-ag it’s find-file
http://ergoemacs.org/emacs/elisp_text_properties.html
how come? was that because of async? probably the async get runs on and buffers that it doesn’t belong to
timer flag can tell if the update should do or not
delete make paste align
make a show mode so its keybindings can be reset by evil local-set-key applies everywhere to major mode anywhere, not recommended it applies to outline mode, it will apply to org mode because org mode is based on outline mode.
(add-hook 'find-file-hook 'virtual-comment-mode)
(add-hook 'virtual-comment-show-mode 'outline-minor-mode)
(evilified-state-evilify virtual-comment-show-mode virtual-comment-show-mode-map
"q" quit-window)
(spacemacs/declare-prefix "cv" "virtual-comments")
(spacemacs/set-leader-keys
"cvv" #'virtual-comment-make
"cvd" #'virtual-comment-delete
"cvs" #'virtual-comment-show
"cvj" #'virtual-comment-next
"cvn" #'virtual-comment-next
"cvN" #'virtual-comment-previous
"cvk" #'virtual-comment-previous
"cvp" #'virtual-comment-paste
"cvr" #'virtual-comment-realign)
unconfirmed
revert-buffer does it work like we open the file again? it is seems to be the case
revert-buffer default option will reload all mode but auto-revert-buffer won’t. it keeps current modes
we need to handle after-revert-buffer-hook
but we can’t tell hard and soft reload apart. Yes we set the flag virtual-comment–is-initialized
when undo applies to region having comments, we lose them
- [x] we only persist to project .evc when the last buffer closed
- [x] must have a function to replace active data with data loaded from store
- first call virtual-comment–reload-project once for the project
- next each buffer calls virtual-comment–reload-data
- [ ] get all buffers belonging to the project and run the init again in a with-current-buffer. This way alleviates the doubled pubsub between buffers and store
(point comment target)
and then before any update or a timer we run a function to reconcile any mismatch between point and line-content then find the right line and move the ov to it
(thing-at-point ‘line t)
ov has virtual-comment virtual-comment-target
virtual-comment–ovs-to-cmts could be the place to reconcile the change
and a notification inside ov can be added and the ov can self correct when there is change inside it and update its position and its virtual properties
if so we don’t need the reconcile process anymore
line at point or overlay where do we assign the point value that’s where we pick up the target but point can be moved but target can be fixed on created
- point can be moved freely
- target should be fixed
- [x] target created by -make
- [x] target can be changed by -paste
- [x] target can be changed by repair fn
org-open-at-point which calls org-link-open-as-file finally (org-link-search ”When”)
(words (split-string s))) (s-multi-re (mapconcat #’regexp-quote words “\(?:[ \t\n]+\)”))
((catch :fuzzy-match (goto-char (point-min)) (while (re-search-forward s-multi-re nil t) ;; Skip match if it contains AVOID-POS or it is included in ;; a link with a description but outside the description. (unless (or (and avoid-pos (<= (match-beginning 0) avoid-pos) (> (match-end 0) avoid-pos)) (and (save-match-data (org-in-regexp org-link-bracket-re)) (match-beginning 3) (or (> (match-beginning 3) (point)) (<= (match-end 3) (point))) (org-element-lineage (save-match-data (org-element-context)) ‘(link) t))) (goto-char (match-beginning 0)) (setq type ‘fuzzy) (throw :fuzzy-match t))) nil)) file:virtual-comment.el::unless (string= org-target current-target
this is a headache for (virtual-comment–get-overlay-at point) we use (overlays-at point) to get all overlays but this function can’t get empty overlays. (overlays-in point point) can get the empty overlays but then it can’t get overlays when at the beginning of the overlay start
we may risk to use save-excursion to check for empty line which is not clean. fix is (overlays-in point (1+ point)), may need to consider the point-max
it seems that kill-buffer-hook is not triggered in this case
insprired by flycheck-global-teardown we would do the same add have a function to kill buffer hook this function will go through the buffer list if mode is active it will call
a function to produce this string: symbol | project-file-path:line-number a function to get read the string and extract project-file-path:line-number then allows us to go therehow can we add the string to the comment? we store the string in a list ((string . project)) then we invoke a function to append the string to the comment
on emacs 28 read-from-minibuffer will be unable to focus to the prompt on first try if the prompt or the initial text has new line character. (read-from-minibuffer “what: \n”)
this is because emacs 28 has a new behavior for minibuffer
Improved handling of minibuffers on switching frames. By default, when you switch to another frame, an active minibuffer now moves to the newly selected frame. Nevertheless, the effect of what you type in the minibuffer happens in the frame where the minibuffer was first activated. An alternative behavior is available by customizing ‘minibuffer-follows-selected-frame’ to nil. Here, the minibuffer stays in the frame where you first opened it, and you must switch back to this frame to continue or abort its command. The old behavior, which mixed these two, can be approximated by customizing ‘minibuffer-follows-selected-frame’ to a value which is neither nil nor t.
The old behavior is desired (setq minibuffer-follows-selected-frame nil)
probably need to move away from minibuffer to prompt for a block of text string-edit and phantom-inline-comment are examples
run a callback
on evcs buffer get the virtual-comment-unit at point
remove it from virtual-comment-buffer-data
delete the the comment and the target line in the buffer
check if the target buffer is open. If so trigger update on that buffer
default case when handle non project files
the best way is to have evc data per git branch: .ecv.branh-name
but it would be tedious how to transfer common data accross branch
if we can’t place them then put them in a lost and found place
solution: diff data and validate saved data
how to tell if data is changed?
- when you add a comment then mark a flag
- but comments can be moved a round without user action due to file changes
what now?
- each comment emit and event on change? too complicated
- can we diff the project data? yes we can
- before persist data we diff the data if they are different then back up and save otherwise do nothing
https://stackoverflow.com/questions/18180393/compare-hash-table-in-emacs-lisp
(defun hash-equal (hash1 hash2) "Compare two hash tables to see whether they are equal." (and (= (hash-table-count hash1) (hash-table-count hash2)) (catch 'flag (maphash (lambda (x y) (or (equal (gethash x hash2) y) (throw 'flag nil))) hash1) (throw 'flag t))))