I'm dealing with a file that has a list of single line json strings. To edit an individual json object, I found this tool: https://github.com/gongo/json-reformat. Now, I'm looking for the reverse operation: given a nicely formatted Json object, collapse it into a single string.
Emacs version: 24.5.1
Doesn't look like json-reformat comes with anything for that.
Here's an interactive function that can do this:
(defun json-to-single-line (beg end)
"Collapse prettified json in region between BEG and END to a single line"
(interactive "r")
(if (use-region-p)
(save-excursion
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
(while (re-search-forward "\\s-+" nil t)
(replace-match " "))))
(print "This function operates on a region")))
Just evaluate the function definition -> highlight the json snippet you want reformatted -> and call this function interactively
The answers from #AesopHimself and #Henschkowski weren't quite right for me as they either didn't eliminate newlines or left double-spaces between elements when a newline and indent was condensed. The json-to-single-line function with the regex below is what works for me:
(defun json-to-single-line (beg end)
"Collapse prettified json in region between BEG and END to a single line"
(interactive "r")
(if (use-region-p)
(save-excursion
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
(while (re-search-forward "[[:space:]\n]+" nil t)
(replace-match " "))))
(print "This function operates on a region")))
The standard json module that comes with current versions of emacs (since 24.4?) has a json-pretty-print function that normally wraps the JSON in a region, but when given a prefix argument minimizes it instead.
So, select the region containing the JSON to minimize and type, C-u M-x json-pretty-print.
This is better than the previous answers because it understands that whitespace inside strings should be untouched.
For me (Emacs 25.1.1), lines in the region were not joined when using the accepted answer. To also remove line endings, I had to extend the regex:
(goto-char (point-min))
(while (re-search-forward "\\s-+\\|\n" nil t)
(replace-match " "))))
Related
I'm building a blog with org-mode publish. But I'm wanting to edit the default style given by org-mode, that edition implies wrap some tags in other tags (like divs tags). So the way I am building this is based some ideas (check the link below).
e.g. The below function it's adding a class attribute to the body tag, and it works! (The idea is based on this question)
(defun my-html-body-onload-filter (output backend info)
"Add class to<body> tag, if any."
(when (and (eq backend 'html)
(string-match "<body>\n" output))
(replace-match "<body class='uk-container-small uk-align-center uk-text-justify'> \n" nil nil output))
)
So the thing I want to do right now, it's wrap img tags in other tags. I'm not a elisp programmer but I'm trying to build a function to reach some solution.
So the actual problem is this:
I have this:
<img src="images/photo.jpg" alt="">
I need to wrap img tags so I can see the next output:
<div uk-lightbox="animation: slide">
<a href="images/photo.jpg">
<img src="images/photo.jpg" alt="">
</a>
</div>
So far I created this little function to extract the src value:
(defun xe ()
(interactive)
(search-forward "<img src=\"")
(defvar x-start (point))
(search-forward "\"")
(backward-char)
(defvar x-end (point))
(kill-ring-save x-start x-end)
)
But I feel like lost, because I don't know much about elisp... so if anyone knows how to keep going (or has the solution) to solve this problem, I would be happy:)
Well Hello there, This was my final solution:
I created this function(Note: as you can see this function is not optimized but works, maybe I will edit this answer to post the final optimized solution):
(defun wrap-img-tags ()
(setq text-to-search-1 "<img src=\"")
(goto-char (point-min))
(setq ntimes (count-matches text-to-search-1))
(if (> ntimes 0)
(cl-loop repeat ntimes do ;; this is like a for loop
(search-forward "<img src=\"" nil t)
(setq x-start (point))
(search-forward "\"")
(backward-char)
(setq x-end (point))
(kill-ring-save x-start x-end)
(beginning-of-line)
(insert (format "\n<div uk-lightbox=\"animation: slide\">
<a href=\"%s\">\n" (car kill-ring)))
(indent-for-tab-command)
(forward-line)
(insert (format "</a>\n</div>"))
)
nil
)
)
Additionally I wanted to add a class attribute on some tags, so I created this next function:
(defun add-class-to-tag (tag class)
"Add class attribute with the class variable value.
TAG: Tag to modify.
CLASS: Class in string form to add."
; (interactive "sTag:\nsClass:")
(setq text-to-search (format "<%s" tag))
(goto-char (point-min))
(setq does-it-have-class-attribute t)
(cl-loop repeat (how-many text-to-search) do ;; this is like a for loop
(search-forward text-to-search)
(setq x-start (point))
(setq does-it-have-class-attribute (search-forward
"class=\""
(line-end-position)
t ; if fails return nil
))
(if (not does-it-have-class-attribute)
(progn
(insert (format " class=\"%s\"" class))
(setq does-it-have-class-attribute nil)
)
(progn ; else
(search-forward "\"")
(backward-char)
(insert (format " %s" class))
))
)
)
Finally I execute it on a pre final processor of the created html files:
(The below function idea was taken from this blog)
(defun org-blog-publish-to-html (plist filename pub-dir)
"Same as `org-html-publish-to-html' but modifies html before finishing."
(let ((file-path (org-html-publish-to-html plist filename pub-dir)))
(with-current-buffer (find-file-noselect file-path)
(wrap-img-tags);; Here I'm executing the first solution
(add-class-to-tag "h2" "uk-heading-bullet")
(add-class-to-tag "section" "uk-card uk-card-body uk-align-center uk-text-justify")
(add-class-to-tag "h1" "uk-h2 uk-panel uk-padding uk-background-secondary uk-light uk-margin-left uk-margin-right")
(save-buffer)
(kill-buffer))
file-path))
And that's it! It works :)
Additionally I would modify the wrap-img-tags function to wrap any kind of tags with other desired tags!
PD: If this answer was helpful to you, please up vote. I spent around three days researching and testing to reach to this solution. Thank you!
I think that you should not save the text. Just insert the stuff that you want.
(while (search-forward "<img src=\"" nil t nil)
(goto-char (match-beginning 0))
(insert "<div uk-lightbox=\"animation: slide\"><a href=\"images/photo.jpg\">")
(search-forward ">")
(insert "</a></div>"))
In case you want to know more about a function use C-h f (describe-function).
I'm writing a lisp app to create 2D patterns for a CNC cutter. I have an Excel program that outputs 3-10 short lisp subroutines for individual pieces associated with each pattern, and I can get any number of these subs to load inside my "while" loop and draw the piece, but none of the methods I can get working to create .dxf files from the drawings generated by one of the pattern subs will let me resume the loop. If I pull out the dxfout and erase steps and just let them all draw on top of one another, it works like a charm.
(defun c:CreateDXF (/ owd ofd sdate)
(setq owd (acet-file-cwd))
(acet-file-chdir "P:\\")
(setq ofd (getvar "filedia"))
(setvar "filedia" 0)
(setq sdate (getstring "\nEnter the order start date in YYYYMMDD format: "))
(setq fpath (strcat "P:\\LSP\\" sdate))
(setq wopath (acet-ui-pickdir "Select Work Order Folder" fpath))
(setq flist (vl-directory-files wopath "*.lsp" 1))
(while (> (length flist) 0)
(setq fname (car flist))
(setq wfile (strcat wopath "\\" fname))
(vl-file-rename wfile "P:\\LSP\\temp.lsp")
(load "P:\\LSP\\temp.lsp")
(vl-file-rename "P:\\LSP\\temp.lsp" wfile)
(setq savepath (strcat "P:\\DXF\\" sdate))
(setq savename (strcat (vl-filename-base wfile) ".dxf"))
(setq patt (ssget "x"))
(command "_.dxfout" savepath "_Objects" patt "" "_Version" "2013" "16" "" "")
(command "erase" "all" "")
(setq flist (cdr flist))
);while
(acet-file-chdir owd)
(setvar "filedia" ofd)
(alert "Done")
(princ)
);defun
Running this as written hangs up at the first dxfout step. I can reset the program through the lisp editor, and then file appears in the output directory and the dxf comes up in a new tab, but obviously that exits the loop without processing the rest of the pieces.
Am I missing something obvious? Would it be better to draw all of the pattern pieces in the same drawing as named entities and then batch out each entity at the end?
It might be easier to approach this from outside of AutoCAD. Maybe using a PowerShell script to fetch the .dwg files list, open AutoCAD, then loop through the drawing list, export the .dxf, close the .dwg and do the next, then close AutoCAD.
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.
hello I am making some word searching program
for example
when "text.txt" file contains "foo foos foor fo.. foo fool"
and search "foo"
then only number 2 printed
and search again and again
but I am haskell beginner
my code is here
:module +Text.Regex.Posix
putStrLn "type text file"
filepath <- getLine
data <- readFile filepath
--1. this makes <interactive>:1:1: parse error on input `data' how to fix it?
parsedData =~ "[^- \".,\n]+" :: [[String]]
--2. I want to make function and call it again and again
searchingFunc = do putStrLn "search for ..."
search <- getLine
result <- map (\each -> if each == search then count = count + 1) data
putStrLn result
searchingFunc
}
sorry for very very poor code
my development environment is Windows XP SP3 WinGhci 1.0.2
I started the haskell several hours ago sorry
thank you very much for reading!
edit: here's original scheme code
thanks!
#lang scheme/gui
(define count 0)
(define (search str)
(set! count 0)
(map (λ (each) (when (equal? str each) (set! count (+ count 1)))) data)
(send msg set-label (format "~a Found" count)))
(define path (get-file))
(define port (open-input-file path))
(define data '())
(define (loop [line (read-line port)])
(when (not (eof-object? line))
(set! data (append data
(regexp-match* #rx"[^- \".,\n]+" line)))
(loop)))
(loop)
(define (cb-txt t e) (search (send t get-value)))
(define f (new frame% (label "text search") (min-width 300)))
(define txt (new text-field% (label "type here to search") (parent f) (callback (λ (t e) (cb-txt t e)))))
(define msg (new message% (label "0Found ") (parent f)))
(send f show #t)
I should start by iterating what everyone would (and should) say: Start with a book like Real World Haskell! That said, I'll post a quick walkthrough of code that compiles, and hopefully does something close to what you originally intended. Comments are inline, and hopefully should illustrate some of the shortcomings of your approach.
import Text.Regex.Posix
-- Let's start by wrapping your first attempt into a 'Monadic Action'
-- IO is a monad, and hence we can sequence 'actions' (read as: functions)
-- together using do-notation.
attemptOne :: IO [[String]]
-- ^ type declaration of the function 'attemptOne'
-- read as: function returning value having type 'IO [[String]]'
attemptOne = do
putStrLn "type text file"
filePath <- getLine
fileData <- readFile filePath
putStrLn fileData
let parsed = fileData =~ "[^- \".,\n]+" :: [[String]]
-- ^ this form of let syntax allows us to declare that
-- 'wherever there is a use of the left-hand-side, we can
-- substitute it for the right-hand-side and get equivalent
-- results.
putStrLn ("The data after running the regex: " ++ concatMap concat parsed)
return parsed
-- ^ return is a monadic action that 'lifts' a value
-- into the encapsulating monad (in this case, the 'IO' Monad).
-- Here we show that given a search term (a String), and a body of text to
-- search in, we can return the frequency of occurrence of the term within the
-- text.
searchingFunc :: String -> [String] -> Int
searchingFunc term
= length . filter predicate
where
predicate = (==)term
-- ^ we use function composition (.) to create a new function from two
-- existing ones:
-- filter (drop any elements of a list that don't satisfy
-- our predicate)
-- length: return the size of the list
-- Here we build a wrapper-function that allows us to run our 'pure'
-- searchingFunc on an input of the form returned by 'attemptOne'.
runSearchingFunc :: String -> [[String]] -> [Int]
runSearchingFunc term parsedData
= map (searchingFunc term) parsedData
-- Here's an example of piecing everything together with IO actions
main :: IO ()
main = do
results <- attemptOne
-- ^ run our attemptOne function (representing IO actions)
-- and save the result
let searchResults = runSearchingFunc "foo" results
-- ^ us a 'let' binding to state that searchResults is
-- equivalent to running 'runSearchingFunc'
print searchResults
-- ^ run the IO action that prints searchResults
print (runSearchingFunc "foo" results)
-- ^ run the IO action that prints the 'definition'
-- of 'searchResults'; i.e. the above two IO actions
-- are equivalent.
return ()
-- as before, lift a value into the encapsulating Monad;
-- this time, we're lifting a value corresponding to 'null/void'.
To load this code, save it into a .hs file (I saved it into 'temp.hs'), and run the following from ghci. Note: the file 'f' contains a few input words:
*Main Text.Regex.Posix> :l temp.hs
[1 of 1] Compiling Main ( temp.hs, interpreted )
Ok, modules loaded: Main.
*Main Text.Regex.Posix> main
type text file
f
foo foos foor fo foo foo
The data after running the regex: foofoosfoorfofoofoo
[1,0,0,0,1,1]
[1,0,0,0,1,1]
There is a lot going on here, from do notation to Monadic actions, 'let' bindings to the distinction between pure and impure functions/values. I can't stress the value of learning the fundamentals from a good book!
Here is what I made of it. It doesn't does any error checking and is as basic as possible.
import Text.Regex.Posix ((=~))
import Control.Monad (when)
import Text.Printf (printf)
-- Calculates the number of matching words
matchWord :: String -> String -> Int
matchWord file word = length . filter (== word) . concat $ file =~ "[^- \".,\n]+"
getInputFile :: IO String
getInputFile = do putStrLn "Enter the file to search through:"
path <- getLine
readFile path -- Attention! No error checking here
repl :: String -> IO ()
repl file = do putStrLn "Enter word to search for (empty for exit):"
word <- getLine
when (word /= "") $
do print $ matchWord file word
repl file
main :: IO ()
main = do file <- getInputFile
repl file
Please start step by step. IO in Haskell is hard, so you shouldn't start with file manipulation. I would suggest to write a function that works properly on a given String. That way you can learn about syntax, pattern matching, list manipulation (maps, folds) and recursion without beeing distracted by the do notation (which kinda looks imperative, but isn't, and really needs a deeper understanding).
You should check out Learn you a Haskell or Real World Haskell to get a sound foundation. What you do now is just stumbling in the dark - which may work if you learn languages that are similar to the ones you know, but definitely not for Haskell.