I love using GDB withing emacs. And I most like the configuration that I get with "gdb-many-windows", as seen here:
gdb-many-windows
That said, it's not perfect. I'd like to add a frame for showing the currently running threads. Does anyone know if it's possible to customize the configuration that "gdb-many-windows" gives you? Or, if I can't do that, is their a way to create my own frames AFTER gdb comes up automatically in my .emacs? My ideal setup would have threads, stack trace, local variables and a big source window.
The window layout used by gdb-many-windows is apparently implemented in gdb-setup-windows. You can advise that function to do additional work in setting up windows, like
(defadvice gdb-setup-windows (around setup-more-gdb-windows activate)
ad-do-it
(split-window-horizontally)
(other-window 1)
(gdb-set-window-buffer
(gdb-get-buffer-create 'gdb-some-buffer-type)))
It is a very old post, however the follow solution can help someone.
The follow code capture the start and the exit of the gdb interface, changing its behavior.
At gdb start (defadvice gdb), it first save the current layout, then run the gdb and finaly prepare a new custom layout (it easy to edit it for your preferences)
At gdb exit (defadvice gdb-reset), it first execute the original exit function and then reload the saved layout.
The result contains the same window of the gdb-many-windows, the current running thread are in the top-right of the image
(setq gdb-many-windows nil)
(defun set-gdb-layout(&optional c-buffer)
(if (not c-buffer)
(setq c-buffer (window-buffer (selected-window)))) ;; save current buffer
;; from http://stackoverflow.com/q/39762833/846686
(set-window-dedicated-p (selected-window) nil) ;; unset dedicate state if needed
(switch-to-buffer gud-comint-buffer)
(delete-other-windows) ;; clean all
(let* (
(w-source (selected-window)) ;; left top
(w-gdb (split-window w-source nil 'right)) ;; right bottom
(w-locals (split-window w-gdb nil 'above)) ;; right middle bottom
(w-stack (split-window w-locals nil 'above)) ;; right middle top
(w-breakpoints (split-window w-stack nil 'above)) ;; right top
(w-io (split-window w-source (floor(* 0.9 (window-body-height)))
'below)) ;; left bottom
)
(set-window-buffer w-io (gdb-get-buffer-create 'gdb-inferior-io))
(set-window-dedicated-p w-io t)
(set-window-buffer w-breakpoints (gdb-get-buffer-create 'gdb-breakpoints-buffer))
(set-window-dedicated-p w-breakpoints t)
(set-window-buffer w-locals (gdb-get-buffer-create 'gdb-locals-buffer))
(set-window-dedicated-p w-locals t)
(set-window-buffer w-stack (gdb-get-buffer-create 'gdb-stack-buffer))
(set-window-dedicated-p w-stack t)
(set-window-buffer w-gdb gud-comint-buffer)
(select-window w-source)
(set-window-buffer w-source c-buffer)
))
(defadvice gdb (around args activate)
"Change the way to gdb works."
(setq global-config-editing (current-window-configuration)) ;; to restore: (set-window-configuration c-editing)
(let (
(c-buffer (window-buffer (selected-window))) ;; save current buffer
)
ad-do-it
(set-gdb-layout c-buffer))
)
(defadvice gdb-reset (around args activate)
"Change the way to gdb exit."
ad-do-it
(set-window-configuration global-config-editing))
Related
I'm working on implementing a neural network to tackle the MNIST dataset in CSV instead of using the images. I'm using Common Lisp with Quicklisp, and the cl-csv utility for CSV parsing. Using cl-csv, how can I return a single row from the CSV? Using (cl-csv:read-csv-row #P"file.csv") this returns row 1. Trying (cl-csv:read-csv-row #P"file.csv" 5) results in: *** - CL-CSV:READ-CSV-ROW: keyword arguments in (#P"test3.csv") should occur pairwise. Can cl-csv return a single specified row, and if so, how do I write the row number as a parameter?
A function that is named like read-… is often thought about as reading from a stream. This involves changing the state of the stream so that the next reading interaction starts where the previous left off. A common idiom is to do this in a loop.
It seems that cl-csv wants the user of read-csv-row to handle end-of-file as a signal, so:
(with-open-file (csv-in-stream csv-pathname)
(handler-case
(loop :for csv-line := (read-csv-row csv-in-stream)
:do (process-somehow csv-line))
(end-of-file () (whatever)))
If you want to get exactly one specific line:
(with-open-file (csv-in-stream csv-pathname)
(handler-case
(loop :repeat (1- n)
:do (read-csv-row csv-in-stream) ; skip skip …
:finally (return (read-csv-row csv-in-stream)))
(end-of-file () (oupsie-file-too-short)))
You'd often want to use one of the provided convenience wrappers:
(do-csv (row csv-pathname)
(process-somehow row))
or using iterate:
(iter
(for row in-csv csv-pathname)
(process-somehow row))
I must admit that I have grown rather fond of the alternative library fare-csv, though.
Solution:
(ql:quickload 'cl-csv) ; load the cl-csv package
(defun nth-csv-row (csv-path n &rest read-csv-row-parameters)
"Return nth line of a csv file, parsed."
(with-open-file (stream csv-path)
(loop for x from 1 below n
do (cl-csv:read-csv-row stream))
(apply #'cl-csv:read-csv-row stream read-csv-row-parameters)))
;; your example executed using the new function:
(nth-csv-row #P"file.csv" 5)
Credits to #Svante, who pointed out a logical mistake I made.
(Originally, I was using do (read-line stream) to skip the lines. But since new-line character can be within csv cells, I have to use cl-csv:read-csv-row to parse the stream correctly for the cases that cells contain new-lines. Thank you #Svante!
The wrong(!) old solution (for educational purposes only):
(ql:quickload 'cl-csv) ; load the cl-csv package
;; a more general function returning the nth line of a file
(defun nth-line (file-path n)
(with-open-file (stream file-path)
(loop for x from 1 to (1- n)
do (read-line stream))
(read-line stream)))
;; wrap it with a csv parsing function
(defun nth-csv-line (csv-path n &rest read-csv-row-parameters)
"Return nth line of a csv file, parsed."
(apply #'cl-csv:read-csv-row (nth-line csv-path n) read-csv-row-parameters))
;; your example executed using the new function:
(nth-csv-line #P"file.csv" 5)
(wouldn't parse correctly, if a csv cell would contain a newline character!) - (read-line) doesn't check whether the new-line character is inside a cell or outside a cell.
Anyway - now follows, what I in addition remarked before (still valid):
Since:
[Function] read-csv-row ( stream-or-string &key (separator
separator) (quote quote) (escape quote-escape) &aux current state line llen c elen) => result
Read in a CSV by data-row (which due to quoted newlines may be more
than one line from the stream)
(https://github.com/AccelerationNet/cl-csv/blob/master/DOCUMENTATION.md#read-csv-row)
And since the &rest read-csv-row-parameters passes all further parameters to the cl-csv:read-csv-row function (exactly like R's ...),
the nth-csv-line has the full capabilities of the cl-csv:read-csv-row function. Thus,
This solution works not only with comma-separated, but also with any other delimiter-separated data
Example:
Consider "~/test.csv" with the content:
abc def klm
1 2 3
A B C
(note: this is a tab delimited file rather than a comma separated file)
Parse its second row by:
(nth-csv-row "~/test.csv" 2 :separator #\TAB) ; instead of comma
;; returns - correctly parsed: ;; ("1" "2" "3")
Appendix (installing quicklisp correctly, to run these snippets ...)
If somebody reading this is a newbie wants to try it and has no quicklisp working (I had to figure out it anew - so maybe it saves your time):
;; ;; If quicklisp is not installed, do on terminal:
;; $ wget https://beta.quicklisp.org/quicklisp.lisp
;; ;; Then in your lisp interpreter:
;; (quicklisp-quickstart:install)
;; ;; following instructions of quickslisp do
;; (load "~/quicklisp/setup.lisp") ; or: path/to/your/quicklisp/setup.lisp
;; With installed quicklisp, you can from now on install and load
;; any quicklisp-able package by:
(ql:quickload 'cl-csv) ; install cl-csv using quicklisp
I've just discovered the shr package in emacs 24.5.1
i.e.
C-x C-f anyfile.html
M-x shr-render-buffer
Looks really good - just what I was after
Can I automate emacs to call shr-render-buffer when I open any .htm or .html file?
UPDATE
I've tried adding the following to my .emacs:
(add-to-list 'auto-mode-alist '("[.]htm$" . shr-render-buffer))
(add-to-list 'auto-mode-alist '("[.]html$" . shr-render-buffer))
but I get the error:
File mode specification error: (void-function shr-render-buffer)
The html file then gets opened in Fundamental mode and it looks even worse than HTML mode
It seems you want to run the function shr-render-buffer automatically once a html file is opened. As you said, the mode for .htm/.html is html-mode by default, you can add the function invocation to the html-mode-hook, such as:
(add-hook 'html-mode-hook '(lambda() (shr-render-buffer (current-buffer))))
As #lawlist pointed, put it after (require 'shr).
As this is emacs, the hardest part of doing what you want is deciding on what is the best approach. This largely depends on personal taste/workflows. I would highly recommend looking at the browse-url package in more detail. One thing I use is a function which allows me to switch between using eww or my default system browser - this means I can easily render web content either in emacs or in chrome/safari/whatever.
Some years ago, I wrote a utility which would allow me to view a number of different file formats, including rendered html, in emacs. I rarely use this now as doc-view has pretty much replaced most of this functionality and is much better. However, it does show how you can use defadvice to modify the view-file function so that id does different things depending on the file type. Note that as this is old emacs code and emacs has improved, there are probably better ways of doing this now. I also know that the 'advice' stuff has been re-worked, but this legacy stuff still works OK. Should get you started. Note that the functionality for MS doc, docx, pdf etc relies on external executables.
My preferred workflow would be to write a function which allows me to reset the browse-url-browser-function to either eww-browse-url or browse-url-default-browser and bind that to a key. I can then choose to display the html in emacs or the external browser and leverage of all the work already done in browse-url.
(require 'custom)
(require 'browse-url)
;; make-temp-file is part of apel prior to emacs 22
;;(static-when (= emacs-major-version 21)
;; (require 'poe))
(defgroup txutils nil
"Customize group for txutils."
:prefix "txutils-"
:group 'External)
(defcustom txutils-convert-alist
'( ;; MS Word
("\\.\\(?:DOC\\|doc\\)$" doc "/usr/bin/wvText" nil nil nil nil nil)
;; PDF
("\\.\\(?:PDF\\|pdf\\)$" pdf "/usr/bin/pdftotext" nil nil nil nil nil)
;; PostScript
("\\.\\(?:PS\\|ps\\)$" ps "/usr/bin/pstotext" "-output" t nil nil nil)
;; MS PowerPoint
("\\.\\(?:PPT\\|ppt\\)$" ppt "/usr/bin/ppthtml" nil nil nil t t))
"*Association for program convertion.
Each element has the following form:
(REGEXP SYMBOL CONVERTER SWITCHES INVERT REDIRECT-INPUT REDIRECT-OUTPUT HTML-OUTPUT)
Where:
REGEXP is a regexp to match file type to convert.
SYMBOL is a symbol to designate the fyle type.
CONVERTER is a program to convert the fyle type to text or HTML.
SWITCHES is a string which gives command line switches for the conversion
program. Nil means there are no switches needed.
INVERT indicates if input and output program option is to be
inverted or not. Non-nil means to invert, that is, output
option first then input option. Nil means do not invert,
that is, input option first then output option.
REDIRECT-INPUT indicates to use < to direct input from the input
file. This is useful for utilities which accept input
from stdin rather than a file.
REDIRECT-OUTPUT indicates to use > to direct output to the output
file. This is useful for utilities that only send output to
stdout.
HTML-OUTPUT Indicates the conversion program creates HTML output
rather than plain text."
:type '(repeat
(list :tag "Convertion"
(regexp :tag "File Type Regexp")
(symbol :tag "File Type Symbol")
(string :tag "Converter")
(choice :menu-tag "Output Option"
:tag "Output Option"
(const :tag "None" nil)
string)
(boolean :tag "Invert I/O Option")
(boolean :tag "Redirect Standard Input")
(boolean :tag "Redirect Standard Output")
(boolean :tag "HTML Output")))
:group 'txutils)
(defun txutils-run-command (cmd &optional output-buffer)
"Execute shell command with arguments, putting output in buffer."
(= 0 (shell-command cmd (if output-buffer
output-buffer
"*txutils-output*")
(if output-buffer
"*txutils-output*"))))
(defun txutils-quote-expand-file-name (file-name)
"Expand file name and quote special chars if required."
(shell-quote-argument (expand-file-name file-name)))
(defun txutils-file-alist (file-name)
"Return alist associated with file of this type."
(let ((al txutils-convert-alist))
(while (and al
(not (string-match (caar al) file-name)))
(setq al (cdr al)))
(if al
(cdar al)
nil)))
(defun txutils-make-temp-name (orig-name type-alist)
"Create a temp file name from original file name"
(make-temp-file (file-name-sans-extension
(file-name-nondirectory orig-name)) nil
(if (nth 7 type-alist)
".html"
".txt")))
(defun txutils-build-cmd (input-file output-file type-alist)
"Create the command string from conversion alist."
(let ((f1 (if (nth 3 type-alist)
output-file
input-file))
(f2 (if (nth 3 type-alist)
input-file
output-file)))
(concat
(nth 1 type-alist)
(if (nth 2 type-alist) ; Add cmd line switches
(concat " " (nth 2 type-alist)))
(if (nth 4 type-alist) ; redirect input (which may be output
(concat " < " f1) ; if arguments are inverted!)
(concat " " f1))
(if (nth 5 type-alist) ; redirect output (see above comment)
(concat " > " f2)
(concat " " f2)))))
(defun txutils-do-file-conversion (file-name)
"Based on file extension, convert file to text. Return name of text file"
(interactive "fFile to convert: ")
(let ((f-alist (txutils-file-alist file-name))
output-file)
(when f-alist
(message "Performing file conversion for %s." file-name)
(setq output-file (txutils-make-temp-name file-name f-alist))
(message "Command: %s" (txutils-build-cmd file-name output-file f-alist))
(if (txutils-run-command
(txutils-build-cmd (txutils-quote-expand-file-name file-name)
(txutils-quote-expand-file-name
output-file) f-alist))
output-file
file-name))))
(defadvice view-file (around txutils pre act comp)
"Perform file conversion or call web browser to view contents of file."
(let ((file-arg (ad-get-arg 0)))
(if (txutils-file-alist file-arg)
(ad-set-arg 0 (txutils-do-file-conversion file-arg)))
(if (string-match "\\.\\(?:HTML?\\|html?\\)$" (ad-get-arg 0))
(browse-url-of-file (ad-get-arg 0))
ad-do-it)))
(provide 'init-text-convert)
I have some JSON files and I'm writing a mode that allows editing a single property of the JSON object independently from the rest. For example:
foo.json:
{
"creation_timestamp": "1411210038.000000",
"description": "lorem ipsum.\ndolor sit amet.",
"version": 4
}
Opening foo.json results in this buffer:
lorem ipsum.
dolor sit amet.
Changing the first line to "foo bar" and saving the file results in a foo.json with only the description field updated:
{
"creation_timestamp": "1411210038.000000",
"description": "foo bar.\ndolor sit amet.",
"version": 4
}
What's the best strategy for this? My current attempt is so:
open the JSON file with find-file
create an invisible overlay from point-min to point-max
parse json
insert the value of the description property at point-min, creating a "view"
add a local-write-file hook and an after-save hook
The local-write-file hook kills the "view", updates the json in the overlay, and saves the file. The after-save hook recreates the "view" so the user can keep editing.
This is long-winded and brittle. Is there a better way of working with data where the screen representation should be different than the disk representation?
You can define your own encoding and decoding in format-alist for that purpose.
Your example could be implemented in the following way:
(defvar-local my-head nil
"Header of json file cut off by json-descr format.")
(defvar-local my-tail nil
"Tail of json file cut off by json-descr format.")
(defun my-from-fn (BEGIN END)
"`format-alist'"
(save-restriction
(narrow-to-region BEGIN END)
(goto-char (point-min))
(let* ((b (re-search-forward "^[[:blank:]]*\"description\":[[:blank:]]*\"" nil t))
(e (ignore-errors (1- (scan-sexps (1- b) 1)))))
(unless (and b e)
(error "Error in original mode")) ;;< TODO some more sensible error message
;; Save head and tail and delete corresponding buffer regions:
(setq-local my-head (buffer-substring-no-properties (point-min) b))
(setq-local my-tail (buffer-substring-no-properties e (point-max)))
(delete-region e (point-max))
(delete-region (point-min) b)
;; Formatting:
(goto-char (point-min))
(while (search-forward "\\n" nil t)
(replace-match "\n"))
)
(point-max) ;;< required by `format-alist'
))
(defun my-to-fn (BEGIN END BUFFER)
"`format-alist'"
(save-restriction
(narrow-to-region BEGIN END)
;; Formatting:
(goto-char (point-min))
(while (search-forward "\n" nil t)
(replace-match "\\\\n"))
;; Insert head and tail:
(let ((head (with-current-buffer BUFFER my-head))
(tail (with-current-buffer BUFFER my-tail)))
(goto-char (point-min))
(insert head)
(goto-char (point-max))
(insert tail))
(point-max)))
(add-to-list 'format-alist
'(json-descr
"File format for editing a single property of a json object."
nil
my-from-fn
my-to-fn
t ; MODIFY: my-to-fn modifies the buffer
nil
nil))
(define-derived-mode my-mode fundamental-mode "JDescr"
"Major mode for editing json description properties."
(format-decode-buffer 'json-descr))
Actually, one can also interpret this as a more general problem. Load a file into a hidden buffer. Use another visible buffer to edit its transformed content. At saving the visible buffer actually transform the content back to the original format and save the hidden buffer.
I do not have the time right now to implement the general case as described above.
The following code roughly covers your special case. (Note, that it is a fast hack for demonstration purposes only.)
(defvar-local original-mode-other nil
"Other buffer related to the current one.")
(define-derived-mode original-mode special-mode ""
"Opens file in invisible auxiliary buffer."
(let* ((b (re-search-forward "^[[:blank:]]*\"description\":[[:blank:]]*\"" nil t))
(e (ignore-errors (1- (scan-sexps (1- b) 1))))
(original-name (buffer-name))
(original-buffer (current-buffer))
str)
(unless (and b e)
(error "Error in original mode")) ;; TODO some more sensible error message
(narrow-to-region b e)
(setq str (buffer-substring-no-properties b e))
(rename-buffer (concat " *" original-name))
(with-current-buffer (switch-to-buffer (get-buffer-create original-name))
;; Set-up the clone buffer for editing the transformed content:
(set-visited-file-name (buffer-file-name original-buffer) t)
(setq original-mode-other original-buffer)
(insert str)
(set-buffer-modified-p nil)
;; Transform content to the format of the clone buffer:
(goto-char (point-min))
(while (search-forward "\\n" nil t) ;; TODO: Skip escaped \n.
(replace-match "\n"))
(add-to-list 'write-contents-functions (lambda ()
;; Transfer content to original buffer
(let ((str (buffer-substring-no-properties (point-min) (point-max))))
(with-current-buffer original-mode-other
(let ((inhibit-read-only t))
(delete-region (point-min) (point-max))
(insert str)
(goto-char (point-min))
;; Transform content to the format of the original buffer:
(while (search-forward "\n" nil t)
(replace-match "\\\\n"))
(save-buffer)
)))
(set-buffer-modified-p nil)
t))
(add-hook 'kill-buffer-hook (lambda ()
(kill-buffer original-mode-other)) t t)
)))
Is your use case really as simple as the scenario you describe (not the solution outline, but the problem/use case)?
If so, your solution sounds like overkill. If the use case is as simple as editing the value of a particular key, I would probably do this:
Display the content of that field (value corresponding to the key) in a temporary buffer, for editing.
Bind a key (e.g., C-c C-c) to save the edited value back to the file.
I do that in Bookmark+ for editing a bookmark's tags, for instance (and also for editing all of a bookmark's fields, using a different command). The command to edit the tags is bmkp-edit-tags. The command (bound to C-c C-c in the edit buffer) is bmkp-edit-tags-send. The code is here, in context. Here it is, out of context:
(defmacro bmkp-with-output-to-plain-temp-buffer (buf &rest body)
"Like `with-output-to-temp-buffer', but with no `*Help*' navigation stuff."
`(unwind-protect
(progn
(remove-hook 'temp-buffer-setup-hook 'help-mode-setup)
(remove-hook 'temp-buffer-show-hook 'help-mode-finish)
(with-output-to-temp-buffer ,buf ,#body))
(add-hook 'temp-buffer-setup-hook 'help-mode-setup)
(add-hook 'temp-buffer-show-hook 'help-mode-finish)))
(define-derived-mode bmkp-edit-tags-mode emacs-lisp-mode
"Edit Bookmark Tags"
"Mode for editing bookmark tags.
When you have finished composing, type \\[bmkp-edit-tags-send]."
:group 'bookmark-plus)
;; This binding must be defined *after* the mode, so `bmkp-edit-tags-mode-map' is defined.
;; (Alternatively, we could use a `defvar' to define `bmkp-edit-tags-mode-map' before
;; calling `define-derived-mode'.)
(define-key bmkp-edit-tags-mode-map "\C-c\C-c" 'bmkp-edit-tags-send)
(defun bmkp-edit-tags (bookmark) ; Bound to `C-x p t e'
"Edit BOOKMARK's tags, and maybe save the result.
The edited value must be a list each of whose elements is either a
string or a cons whose key is a string.
BOOKMARK is a bookmark name or a bookmark record."
(interactive (list (bookmark-completing-read "Edit tags for bookmark" (bmkp-default-bookmark-name))))
(setq bookmark (bmkp-get-bookmark-in-alist bookmark))
(let* ((btags (bmkp-get-tags bookmark))
(bmkname (bmkp-bookmark-name-from-record bookmark))
(edbuf (format "*Edit Tags for Bookmark `%s'*" bmkname)))
(setq bmkp-return-buffer (current-buffer))
(bmkp-with-output-to-plain-temp-buffer edbuf
(princ
(substitute-command-keys
(concat ";; Edit tags for bookmark\n;;\n;; \"" bmkname "\"\n;;\n"
";; The edited value must be a list each of whose elements is\n"
";; either a string or a cons whose key is a string.\n;;\n"
";; DO NOT MODIFY THESE COMMENTS.\n;;\n"
";; Type \\<bmkp-edit-tags-mode-map>`\\[bmkp-edit-tags-send]' when done.\n\n")))
(let ((print-circle bmkp-propertize-bookmark-names-flag)) (pp btags))
(goto-char (point-min)))
(pop-to-buffer edbuf)
(buffer-enable-undo)
(with-current-buffer (get-buffer edbuf) (bmkp-edit-tags-mode))))
(defun bmkp-edit-tags-send (&optional batchp)
"Use buffer contents as the internal form of a bookmark's tags.
DO NOT MODIFY the header comment lines, which begin with `;;'."
(interactive)
(unless (eq major-mode 'bmkp-edit-tags-mode) (error "Not in `bmkp-edit-tags-mode'"))
(let (bname)
(unwind-protect
(let (tags bmk)
(goto-char (point-min))
(unless (search-forward ";; Edit tags for bookmark\n;;\n;; ")
(error "Missing header in edit buffer"))
(unless (stringp (setq bname (read (current-buffer))))
(error "Bad bookmark name in edit-buffer header"))
(unless (setq bmk (bmkp-get-bookmark-in-alist bname 'NOERROR))
(error "No such bookmark: `%s'" bname))
(unless (bmkp-bookmark-type bmk) (error "Invalid bookmark"))
(goto-char (point-min))
(setq tags (read (current-buffer)))
(unless (listp tags) (error "Tags sexp is not a list of strings or an alist with string keys"))
(bookmark-prop-set bmk 'tags tags)
(setq bname (bmkp-bookmark-name-from-record bmk))
(bmkp-record-visit bmk batchp)
(bmkp-refresh/rebuild-menu-list bname batchp)
(bmkp-maybe-save-bookmarks)
(unless batchp (message "Updated bookmark file with edited tags")))
(kill-buffer (current-buffer)))
(when bmkp-return-buffer
(pop-to-buffer bmkp-return-buffer)
(when (equal (buffer-name (current-buffer)) "*Bookmark List*")
(bmkp-bmenu-goto-bookmark-named bname)))))
The most relevant bits are these:
Define a command to initiate editing and a command to end it and save the changes.
Provide an edit buffer using bmkp-with-output-to-plain-temp-buffer (essentially with-output-to-temp-buffer, but that macro in some Emacs versions also adds Help mode stuff not needed here).
Put the edit buffer in a simple minor mode that binds C-c C-c to the save-and-exit command.
Fill the edit buffer with the text to be edited. Pop to the buffer, for editing.
In the save-and-exit command (bmkp-edit-tags-send), update the original data, replacing the relevant field contents with the edit-buffer contents. Save the updated data. Return to the original buffer.
I am learning Clojure and trying to implement a simple tic-tac-toe (or morpion). But i am working hard in order to avoid any ref/atom/agent ...
I know I could do it easily in a console application, as it would not be event-driven, so that I would exactly know when to pass in a new board value. I mean
my board would be a vector of vectors ([[:circle 0 :cross][:any :circle :empty][:cross :none :none]] where only :circle and :cross values matter)
I would have a simple text-based method which takes a board and return a string
But, whenever I want to implement a graphical Panel, I wonder how can I do the same thing. For example :
I am creating an instance of javax.swing.JPanel (please don't care about the other methods that are used here) :
(defn make-board-panel
"Builds the board JPanel, whose state is given by board argument,
and turn-piece argument (must be either :circle or :cross, I mean the next
value to set in the board)."
[board turn-piece]
{:pre ['morpion.core/is-board? board, 'morpion.core/is-a-player-piece? turn-piece]}
(proxy [JPanel MouseListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint-board-lines g)
(paint-board board g)
)
(mouseClicked [e]
(if (and (abs-coord-in-cell? (.getX e)) (abs-coord-in-cell? (.getY e)))
(let [cell-x (abs-coord-to-rel (.getX e))
cell-y (abs-coord-to-rel (.getY e))]
;; Here I compute the new board value
)
nil ;; Here I wish I could return the new board value to the caller of make-board-panel, but this seems impossible !
)
)
(mouseEntered [e])
(mouseExited [e])
(mousePressed [e])
(mouseReleased [e])
)
)
But it seems that there are no way for me to fetch the new value of the board, from the mouseClicked event of the Panel : unless I introduce a state variable.
So is there a workaround :
Sources for my complete project :
Morpion Core
Morpion Graphic.
(I've tried to improve thanks to Igrapenthin comment, but I am still failing.)
(defn make-board-panel
[board turn-piece output-fn]
(proxy [JPanel MouseListener] []
;; ...
(mouseClicked [e]
(when (and (abs-coord-in-cell? (.getX e))
(abs-coord-in-cell? (.getY e)))
(let [cell-x (abs-coord-to-rel (.getX e))
cell-y (abs-coord-to-rel (.getY e))]
;; Here I compute the new board value
(output-fn new-board-value))))
;; ...
))
I'd like to change the colors of all the built-in byobu plugins/indicators to match my solarized palette for tmux/vim/emacs/terminal.
Is there an easy way to override them in the user's rc file or would I have to change them in /usr/lib/byobu/* ?
This is possible, in fact.
You'll want to grab the definition of the color_map() function, which is defined in /usr/lib/byobu/include/shutil.
Copy that entire function and paste it into your local user's ~/.byobu/color.tmux configuration file. That function currently looks something like this:
color_map() {
case "$1" in
"k") _RET="black" ;;
"r") _RET="red" ;;
"g") _RET="green" ;;
"y") _RET="yellow" ;;
"b") _RET="blue" ;;
"m") _RET="magenta" ;;
"c") _RET="cyan" ;;
"w") _RET="white" ;;
"d") _RET="black" ;;
"K") _RET="brightblack" ;;
"R") _RET="brightred" ;;
"G") _RET="brightgreen" ;;
"Y") _RET="brightyellow" ;;
"B") _RET="brightblue" ;;
"M") _RET="brightmagenta" ;;
"C") _RET="brightcyan" ;;
"W") _RET="brightwhite" ;;
*) _RET= ;;
esac
}
Now, you can change those colors, as you like. For instance, if you change the "Y" definition from "brightyellow" to "magenta", you should see your system load indicator go from yellow to purple.
If you're using solarized, you'll probably want to use Tmux's 256-color support. In which case, you'll probably want to specify a 256-color choice rather than a 16-color choice, such as "color52" You can use this xterm color chart as a guide:
And if you do create a solarized theme, please do share it, as I would be happy to include it in [Byobu][2] as an option!
Full disclosure: I am the author and maintainer of Byobu.