function in vim to fix quotations - function

I'm trying to create a function in vim to be activated by a shortcut and go through my document and re-quote the strings using Python-like syntax. That is:
"something" -> 'something'
'''docstring''' -> """docstring"""
"'" -> "'" (stays the same)
'"' -> '"' (stays the same)
Given my limited knowledge of python functions I came up with this in my .vimrc:
function! Fixquotes()
:silent! %s/"\([^"]*\)"/'\1'/g
:silent! %s/'""/"""/
:silent! %s/""'/"""/
:silent! %s/'''/"""/g
endfunction
inoremap <C-f> <esc>mk:call Fixquotes()<CR>`kli
noremap <C-f> mk:call Fixquotes()<CR>`k
It kinda work, except for the case where I have "'" since the first substitution will turn it into ''' and the last will turn it into """.
Does anyone have any recommendation?

give this a try:
function! Fixquotes()
:silent! %s/\v([^'"]|\_^)\zs"([^"']*)"\ze([^'"]|\_$)/'\2'/g
:silent! %s/'''/"""/g
endfunction

Related

LISP: how to properly encode a slash ("/") with cl-json?

I have code that uses the cl-json library to add a line, {"main" : "build/electron.js"} to a package.json file:
(let ((package-json-pathname (merge-pathnames *app-pathname* "package.json")))
(let
((new-json (with-open-file (package-json package-json-pathname :direction :input :if-does-not-exist :error)
(let ((decoded-package (json:decode-json package-json)))
(let ((main-entry (assoc :main decoded-package)))
(if (null main-entry)
(push '(:main . "build/electron.js") decoded-package)
(setf (cdr main-entry) "build/electron.js"))
decoded-package)))))
(with-open-file (package-json package-json-pathname :direction :output :if-exists :supersede)
(json:encode-json new-json package-json))
)
)
The code works, but the result has an escaped slash:
"main":"build\/electron.js"
I'm sure this is a simple thing, but no matter which inputs I try -- "//", "/", "#//" -- I still get the escaped slash.
How do I just get a normal slash in my output?
Also, I'm not sure if there's a trivial way for me to get pretty-printed output, or if I need to write a function that does this; right now the output prints the entire package.json file to a single line.
Special characters
The JSON Spec indicates that "Any character may be escaped.", but some of them MUST be escaped: "quotation mark, reverse solidus, and the control characters". The linked section is followed by a grammar that show "solidus" (/) in the list of escaped characters. I don't think it is really important in practice (typically it needs not be escaped), but that may explain why the library escapes this character.
How to avoid escaping
cl-json relies on an internal list of escaped characters named +json-lisp-escaped-chars+, namely:
(defparameter +json-lisp-escaped-chars+
'((#\" . #\")
(#\\ . #\\)
(#\/ . #\/)
(#\b . #\Backspace)
(#\f . #\)
(#\n . #\Newline)
(#\r . #\Return)
(#\t . #\Tab)
(#\u . (4 . 16)))
"Mapping between JSON String escape sequences and Lisp chars.")
The symbol is not exported, but you can still refer to it externally with ::. You can dynamically rebind the parameter around the code that needs to use a different list of escaped characters; for example, you can do as follows:
(let ((cl-json::+json-lisp-escaped-chars+
(remove #\/ cl-json::+json-lisp-escaped-chars+ :key #'car)))
(cl-json:encode-json-plist '("x" "1/5")))
This prints:
{"x":"1/5"}

vimscript: commands that work in mappings, but not in functions

How can I rewrite these 2 commands, which work fine in a mapping, so that they will work in a function?
:if has_key(glos,#g)==1<cr>:let #j=eval('glos.'.#g)<cr>
The function concerned is executed by vim without comment, but #j remains unchanged, as if they had failed, but no message/error is generated.
Here is the complete code involved, the command that loads the dictionary, the function that does not work, and the mapping from that function that does.
" read the glossary into the dictionary, glos
let glos=eval(join(readfile("glossary.dict")))
" 2click item of interest and this will
" send image filepath to xv
" if item all-caps find same at start of its line
" If capitalized at eol find same at start of its line
" if all lower-case at eol find next occurrence of same
" look lower-case or capitalized word up in glossary.txt
" find _\d\+ (page no.) alone on its line in text
com! F call F()
function! F()
normal "ayiw"cyE"by$
let #c=substitute(#c,"[,.?':;!]\+","","g")
if #c=~'images\/ss\d\d\d*'
let #i='!display -geometry +0+0 '.#c.' &'
pkill display
#i
elseif #c==toupper(#c)
let #n=search('^'.#c,'sw')
elseif #c!=#b
let #f=3
let #g=tolower(#c)
while #f>0
try
let #j=eval('glos.'.#g)
catch
let #f=#f-1
let #g=strpart(#g,0,strlen(#g)-1)
continue
endtry
break
endwh
if #f>0
let #h=substitute(#j," glosimgs.*",'','')
if #h!=#j
let #i='!xv -geometry +0+380 '.substitute(#j,'^.\{-}\( glosimgs.*\)$','\1','').' &'
!pkill xv
#i
endif
echo #h
else
echo 'No matching entry for '.#c
endif
elseif #c=~'\u\l\+$'
let #n=search('^'.#c,'sw')
elseif #c=~'\l\+$'
norm *
elseif #c=~'^_\w\+$'
let #/='^'.#c.'$'
norm nzz
endif
endfunction
map <silent> <2-LeftMouse> "ayiw"cyE"by$:let #c=substitute(#c,"[,.?':;!]\+","","g")<cr>:if #c=~'images\/ss\d\d\d*'<cr>:let #i='!display -geometry +0+0 '.#c.' &'<cr>:pkill display<cr>:#i<cr>:elseif #c==toupper(#c)<cr>:let #n=search('^'.#c,'sw')<cr>:elseif #c!=#b<cr>:let #f=3<cr>:let #g=tolower(#c)<cr>:while #f>0<cr>:try<cr>:let #j=eval('glos["'.#g.'"]')<cr>:catch<cr>:let #f=#f-1<cr>:let #g=strpart(#g,0,strlen(#g)-1)<cr>:continue<cr>:endtry<cr>:break<cr>:endwh<cr>:if #f>0<cr>:let #h=substitute(#j," glosimgs.*",'','')<cr>:if #h!=#j<cr>:let #i='!xv -geometry +0+380 '.substitute(#j,'^.\{-}\( glosimgs.*\)$','\1','').' &'<cr>:!pkill xv<cr>:#i<cr>:endif<cr><cr<cr>>:echo #h<cr>:else<cr>:echo 'No matching entry for '.#c<cr>:endif<cr>:elseif #c=~'\u\l\+$'<cr>:let #n=search('^'.#c,'sw')<cr>:elseif #c=~'\l\+$'<cr>:norm *<cr>:elseif #c=~'^_\w\+$'<cr>:let #/='^'.#c.'$'<cr>:norm nzz<cr>:endif<cr>
Specifically, I should have written:
:if has_key(**g:**glos,#g)==1:let #j=eval('**g:**glos.'.#g)
:h g: goes straight to the heart of the matter; in a function all references are local to that function, so references to any variable outside the function must be global, by prepending 'g:' to the variable name. As I created the dictionary independent of the function, the function must reference it as a global item.
Of course, if you are not aware of 'g:', it is rather difficult to find that help reference, but that's a frequent problem using help.
And, of course, the ** surrounding g: aren't required, that's what this site gives you in lieu of bolded text, apparently.

how to insert new line in vimscript substitiute function

I want this sentence:
My, sentence.
To be like this:
My,
sentence.
My function is:
function! ParseLine()
let line = getline(".")
echom line
let parsedLine = substitute(line, ",", "\v\\v\\0\\r\r\\n\r\\<cr>\<cr><cr>\\^M\r&\^#\\^#", "g")
call setline(".", parsedLine)
endfunction
What I get when running this function:
Myvv,^M^M^#^M<cr>^M<cr>^M^M,^#^# sentence.
The easiest way:
function! ParseLine()
exec 's/,\s*/,\r/g'
endfunction
Or if you want to first call substitute() then "set" that line:
function! ParseLine()
let parsedLine = substitute(getline('.'), ',\s*', ',\n', "g")
let o = #o
let #o = parsedLine
normal! V"op
let #o=o
endfunction
The way you are trying to do it is never going to work because you can't use newlines in the setline function.
If you try to use setline on a string that contains a newline you will get an error. However, you could do this with an execute "normal! ..." command:
function! ParseLine()
execute "normal! 0f,a\n"
endfunction
That should find the , and insert a newline after it, giving you the result you want.
Note that you have to use execute "normal! ..." as opposed to normal! ... because normal won't understand that \n is a special character and you would get:
My,\n Sentence

tail() function in XQuery

Is there a way in XQuery to do something like a tail() function?
What I'm trying to accomplish is to get the contents of a file (using "xdmp:filesystem-file($path)") and then display only the last 100 lines. I can't seem to find a good way to do this. Any ideas?
Thank you.
In plain XQuery, this can be accomplished by splitting into lines and getting the desired number of lines from the end of the sequence, then rejoining them, if necessary, i.e.
declare function local:tail($content as xs:string, $number as xs:integer)
{
let $linefeed := "
"
let $lines := tokenize($content, $linefeed)
let $tail := $lines[position() > last() - $number]
return string-join($tail, $linefeed)
};
A pure and short XPath 2.0 solution -- can be used not only in XQuery but in XSLT or in any other PL hosting XPath 2.0:
for $numLines in count(tokenize(., '
'))
return
tokenize(., '
')[position() gt $numLines -100]
Or:
for $numLines in count(tokenize(., '
'))
return
subsequence(tokenize(., '
'), $numLines -100 +1)
if your xdmp:file-xx is a nature of text file then you could use something like
let $f := 'any file system path'
return fn:tokenize(xdmp:filesystem-file($f), '[\n\r]+')[ fn:last() - 2 to fn:last()]
here
i have used newline & carriage return as my token splitter. if you need something else to tokenize u could. but simple log file tailing then this solution works fine.
given example tails last 2 lines of a given file. if you want more than alter fn:last()-2 to fn:last() - x

How to control indentation after an open parenthesis in Emacs

When I use emacs python-mode, if the last character of a line is an open parenthesis it indents the next line just one step in from the indentation of the previous line.
call_some_function(
some_very_long_argument_that_I_want_to_put_on_its_own_line)
I like that. Now in ecmascript-mode (which I am using for actionscript 3), it always indents to the level of the previous parenthesis.
call_some_function(
this_is_not_really_saving_me_any_horizontal_space);
How can I make ecmascript-mode indent like python-mode in this respect?
Since ecmascript-mode is based on cc-mode, you can use c-set-offset which allows you to customize any syntactic symbol's offset with the preferred value.
In your case, go to the point which is indented in the wrong level, hit C-c C-o (or type M-x c-set-offset), accept the suggested symbol (arglist-intro), and set it a new value (e.g. +, the default offset).
You can also do it programmatically in your dotemacs, for instance, with:
(add-hook 'ecmascript-mode-hook
(lambda ()
(c-set-offset 'arglist-intro '+)
(c-set-offset 'arglist-close 0)))
ecmascript-mode seems to be based on cc-mode. If you set the indentation style for cc-mode,
it will also work for ecmascript-mode. I have the following code in my .emacs. When I use
ecmascript-mode it indents as desired:
;;{{{ c/c++ indent style variables
(require 'cc-mode)
(defconst my-c-style
'(
(c-electric-pound-behavior . 'alignleft)
(c-tab-always-indent . t)
(c-hanging-braces-alist . ((block-open)
(brace-list-open)
(substatement-open)
(defun-open before after)
(defun-close before after)
))
(c-hanging-colons-alist . ((member-init-intro before)
(inher-intro)
(case-label)
(access-label after)
(label after)
(access-key after)))
(c-cleanup-list . (scope-operator
empty-defun-braces
defun-close-semi))
(c-offsets-alist . ((arglist-close . c-lineup-arglist)
(case-label . 4)
(statement-case-intro . 4)
(access-label . -4)
(label . -)
(substatement-open . 0)
(block-open . 0)
(knr-argdecl-intro . -)))
)
"My C++/C Programming Style")
; Customizations for both c-mode and c++-mode
(defun my-c-mode-common-hook ()
; set up for my perferred indentation style, but only do it once
(c-add-style "My" my-c-style 'set-this-style)
; we like auto-newline and hungry-delete
(c-toggle-auto-hungry-state 1)
; keybindings for both C and C++. We can put these in c-mode-map
; because c++-mode-map inherits it
(define-key c-mode-map "\C-m" 'newline-and-indent)
; insert 8 tabs
(setq tab-width 8)
)
;;}}}
Thank you Török Gábor, in my case I prefered to set
(add-hook 'XXX-mode-hook
(lambda ()
(c-set-offset 'arglist-cont-nonempty '+)))
I was looking for something like this :
veryLongFunctionName (bar,
bar,
bar)
For a more exhaustive list of variables : read emacs documentation