Vim autocmd based on file contents - latex

I'm trying to set up Vim to detect when a .tex file contains the command '\usepackage{sagemath}', and run a command accordingly. I've gotten to
:au BufReadPost,BufWritePost *.tex TTarget sagepdf
but that will fire for all .tex files, which isn't what I want.

Theres an example in my filetype.vim on how to destinguish html types. You can easily modify to suit your logic. Note the getline(n) =~ lines
" HTML (.shtml and .stm for server side)
au BufNewFile,BufRead *.html,*.htm,*.shtml,*.stm call s:FThtml()
" Distinguish between HTML, XHTML and Django
fun! s:FThtml()
let n = 1
while n < 10 && n < line("$")
if getline(n) =~ '\<DTD\s\+XHTML\s'
setf xhtml
return
endif
if getline(n) =~ '{%\s*\(extends\|block\)\>'
setf html.django_template
" setf htmldjango
return
endif
let n = n + 1
endwhile
setf html
endfun

First, you should consider using a modeline.
If you can't get what you want with a modeline, you can use your own function in autocmd, like this:
function! MyFunction()
...
endfunction
autocmd BufReadPost,BufWritePost *.tex call MyFunction()
and you probably can write a function that checks whether a certain pattern matches, and then runs whatever you want.

Related

PandocLuaError "all choices failed" in custom pandoc writer

I'm trying to develop a pandoc (v2.18) lua custom writer for kramdown. Kramdown uses $$ as delimiter for display and inline math and so my writer looks like:
function Writer (doc, opts)
local filter = {
Math = function(elem)
local math = elem
if elem.mathtype == 'DisplayMath' then
local delimited = '\n$$' .. elem.text ..'$$\n'
math = pandoc.RawBlock('markdown', delimited)
end
if elem.mathtype == 'InlineMath' then
local delimited = '$$' .. elem.text ..'$$'
math = pandoc.RawInline('markdown', delimited)
end
return math
end
}
return pandoc.write(doc:walk(filter), 'markdown', opts)
end
Now when trying to convert a latex test file called vector.tex this fails with the error message
$ pandoc -t kramdown.lua vector.tex -o vector.md --wrap=preserve
Error running Lua:
PandocLuaError "all choices failed"
stack traceback:
kramdown.lua:21: in function 'Writer'
I realized that it works and I get the output I want by replacing RawBlock with RawInline like
math = pandoc.RawInline('markdown', delimited .. '\n')
So there seems to be a problem with my usage of RawBlock. I am new to pandoc and lua so maybe I'm missing something basic here. Can someone give me a hint what might be the issue here?
Using RawInline works, as Math elements are inline elements. Display math may look like a block, but internally it's still an inline. Filters must replace inline elements with other inlines, and blocks with blocks.
A "Block" is something like a paragraph, list, or block quote, while an "Inline" is text, emphasis, an image, or a link.
Sorry for the abysmal error message, I'll try to improve that.

How to change the HTML rendering of a Pandoc element?

I'm trying to customize the default HTML output of footnotes from an .odt file.
For example a file with a footnote like this:
Some text with a footnote1
Will render the HTML output below:
<ol class="footnotes">
<li id="fn1" role="doc-endnote">
<p>Content of footnote number 1. ↩︎</p>
</li>
</ol>
I want instead to have a flat paragraph to be output, with hardcoded a number like following:
<p>1. Content of footnote number 1. ↩︎</p>
I've used parts of sample.lua from the Pandoc repo but is not working, the process is blocked by this error:
$ pandoc --lua-filter=my-filter.lua file.odt -o file.html
Error running filter my-filter.lua:
my-filter.lua:7: bad argument #1 to 'gsub' (string expected, got table)
stack traceback:
[C]: in function 'string.gsub'
my-filter.lua:7: in function 'Note'
Below is my attempted script, I guess I'm naively overlooking something obvious or I've badly understood how filters work.
-- Table to store footnotes, so they can be included at the end.
local notes = {}
function Note(s)
local num = #notes + 1
-- insert the back reference right before the final closing tag.
s = string.gsub(s,
'(.*)</', '%1 ↩</')
-- add a list item with the note to the note table.
table.insert(notes, '<p id="fn' .. num .. '">' .. num .. '. ' .. s .. '</p>')
-- return the footnote reference, linked to the note.
return '<a id="fnref' .. num .. '" href="#fn' .. num ..
'"><sup>' .. num .. '</sup></a>'
end
function Pandoc (doc)
local buffer = {}
local function add(s)
table.insert(buffer, s)
end
add(doc)
if #notes > 0 then
for _,note in pairs(notes) do
add(note)
end
end
return table.concat(buffer,'\n') .. '\n'
end
Update
Tweaking part of what #tarleb answered I've managed now to modify the inline note reference link, but apparently the second function is not rendering the list of footnotes at the end of the document. What's missing?
local notes = pandoc.List{}
function Note(note)
local num = #notes + 1
-- add a list item with the note to the note table.
notes:insert(pandoc.utils.blocks_to_inlines(note.content))
-- return the footnote reference, linked to the note.
return pandoc.RawInline('html', '<a id="fnref' .. num .. '" href="#fn' .. num ..
'"><sup>' .. num .. '</sup></a>')
end
function Pandoc (doc)
doc.meta['include-after'] = notes:map(
function (content, i)
-- return a paragraph for each note.
return pandoc.Para({tostring(i) .. '. '} .. content)
end
)
return doc
end
The sample.lua is an example of a custom Lua writer, not a Lua filter. They can look similar, but are quite different. E.g., filter functions modify abstract document elements, while functions in custom writers generally expect strings, at least in the first argument.
A good way to go about this in a filter could be to place the custom rendering in the include-after metadata:
local notes = pandoc.List{}
function Pandoc (doc)
doc.blocks:walk {
Note = function (note)
notes:insert(pandoc.utils.blocks_to_inlines(note.content))
-- Raw HTML goes into an RawInline element
return pandoc.RawInline('html', 'footnote link HTML goes here')
end
}
doc.meta['include-after'] = notes:map(
function (content, i)
-- return a paragraph for each note.
return pandoc.Para({tostring(i) .. ' '} .. content)
end
)
return doc
end
I've managed after some trial and error to get a result that is working as intended, but "stylistically" not absolutely perfect.
Please read my commentary below mostly as an excercise, I'm trying to understand better how to use this great tool the way I wanted, not the way any reasonable person should in a productive way (or any way at all). ;)
What I'd like to improve:
I have to wrap the p elements in a div because as of Pandoc 2.18 is not possible to provide direct attributes to a Paragraph. This is a minor code bloat but acceptable.
I'd like to use a section element instead of a div to put all the notes at end of document (used in the Pandoc function), but I haven't found a way to create a RawBlock element and then add the note blocks to it.
I'm tottaly not proficient in Lua and barely grasped a few concept of how Pandoc works, so I'm pretty confident that what I've done below is non optimal. Suggestions are welcome!
-- working as of Pandoc 2.18
local notes = pandoc.List{}
function Note(note)
local num = #notes + 1
-- create a paragraph for the note content
local footNote = pandoc.Para(
-- Prefix content with number, ex. '1. '
{tostring(num) .. '. '} ..
-- paragraph accept Inline objects as content, Note content are Block objects
-- and must be converted to inlines
pandoc.utils.blocks_to_inlines(note.content) ..
-- append backlink
{ pandoc.RawInline('html', '<a class="footnote-back" href="#fnref' .. num .. '" role="doc-backlink"> ↩︎</a>')}
)
-- it's not possible to render paragraphs with attribute elements as of Pandoc 2.18
-- so wrap the footnote in a <div> with attributes and append the element to the list
notes:insert(pandoc.Div(footNote, {id = 'fn' .. num, role = 'doc-endnote'}))
-- return the inline body footnote reference, linked to the note.
return pandoc.RawInline('html', '<a id="fnref' .. num .. '" href="#fn' .. num ..
'"><sup>' .. num .. '</sup></a>')
end
function Pandoc (doc)
if #notes > 0 then
-- append collected notes to block list, the end of the document
doc.blocks:insert(
pandoc.Div(
notes:map(
function (note)
return note
end
),
-- attributes
{class = 'footnotes', role = 'doc-endnotes'}
)
)
end
return doc
end

how to find the index of a repeated character in lua string

suppose you have a path like this
/home/user/dev/project
I want to get the index of any / I want
like if I want the one before dev or the one before user
I don't get lua string patterns if there is a good documentation for it please link it
There are several ways to do this. Perhaps the simplest is using the () pattern element which yields a match position combined with string.gmatch:
for index in ("/home/user/dev/project"):gmatch"()/" do
print(index)
end
which prints
1
6
11
15
as expected. Another way to go (which requires some more code) would be repeatedly invoking string.find, always passing a start index.
Assuming that you probably want to split a string by slashes, that's about as simple using string.gmatch:
for substr in ("/home/user/dev/project"):gmatch"[^/]+" do
print(substr)
end
(the pattern finds all substrings of nonzero, maximal length that don't contain a slash)
Documentation for patterns is here. You might want to have a look at the subsection "Captures".
There are many ways to do so.
Also its good to know that Lua has attached all string functions on datatype string as methods.
Thats what #LMD demonstrates with the : directly on a string.
My favorite place for experimenting with such complicated/difficult things like pattern and their captures is the Lua Standalone Console maked with: make linux-readline
So lets play with the pattern '[%/\\][%u%l%s]+'
> _VERSION
Lua 5.4
> -- Lets set up a path
> path='/home/dev/project/folder with spaces mixed with one OR MORE Capitals in should not be ignored'
> -- I am curious /home exists so trying to have a look into
> os.execute('/bin/ls -Ah ' .. ('"%s"'):format(path:match('[%/\\][%u%l%s]+')));
knoppix koyaanisqatsi
> -- OK now lets see if i can capture the last folder with the $
> io.stdout:write(('"%s"\n'):format(path:match('[%/\\][%u%l%s]+$'))):flush();
"/folder with spaces mixed with one OR MORE Capitals in should not be ignored"
> -- Works too so now i want to know whats the depth is
> do local str, count = path:gsub('[%/\\][%u%l%s%_%-]+','"%1"\n') print(str) return count end
"/home"
"/dev"
"/project"
"/folder with spaces mixed with one OR MORE Capitals in should not be ignored"
4
> -- OK seems usefull lets check a windows path with it
> path='C:\\tmp\\Some Folder'
> do local str, count = path:gsub('[%/\\][%u%l%s]+','<%1>') print(str) return count end
C:<\tmp><\Some Folder>
2
> -- And that is what i mean with "many"
> -- But aware that only lower upper and space chars are handled
> -- So _ - and other chars has to be included by the pattern
> -- Like: '[%/\\][%u%l%s%_%-]+'
> path='C:\\tmp\\Some_Folder'
> do local str, count = path:gsub('[%/\\][%u%l%s%_%-]+','<%1>') print(str) return count end
C:<\tmp><\Some_Folder>
2
> path='C:\\tmp\\Some-Folder'
> do local str, count = path:gsub('[%/\\][%u%l%s%_%-]+','<%1>') print(str) return count end
C:<\tmp><\Some-Folder>
2

AppleScript parsing html from site

What I'm trying to do is to get the names of all TV shows on this Wikipedia page.
Ok, so I did this first:
property showsWebList : {}
tell application "Safari"
set loadDelay to 2 -- in seconds; test for your system
make new document at end of every document
set URL of document 1 to "http://en.wikipedia.org/wiki/List_of_television_programs_by_name"
delay loadDelay
set nrOfUls to do JavaScript "document.getElementById('mw-content-text').querySelectorAll('ul').length;" in document 1
set nrOfUls to nrOfUls - 1 as number
log nrOfUls
repeat with ws from 1 to nrOfUls
delay loadDelay
set nrOfLis to do JavaScript "document.getElementById('mw-content-text').getElementsByTagName('UL')[" & ws & "].querySelectorAll('li').length;" in document 1
set nrOfLis to nrOfLis - 1 as number
log nrOfLis
repeat with rs from 0 to nrOfLis
delay 0.3
set aShow to do JavaScript "document.getElementById('mw-content-text').getElementsByTagName('UL')[" & ws & "].getElementsByTagName('LI')[" & rs & "].getElementsByTagName('I')[0].getElementsByTagName('A')[0].innerHTML;" in document 1
if aShow is not "" or "missing value" then
copy aShow to end of showsWebList
end if
end repeat
end repeat
end tell
And this works exactly how I want it to. The problem is that it takes 15 minutes until it's done and you gotta have the safari document in front the whole time. So my thought was to pick up the whole code and parse it. Not that easy. This is how my code looks now:
tell application "Safari"
make new document at end of every document
set URL of document 1 to "http://en.wikipedia.org/wiki/List_of_television_programs_by_name"
delay 4
set orgHTML to do JavaScript "document.getElementById('mw-content-text').innerHTML;" in document 1
set orgHTML to orgHTML as text
set readyText to my extractBetween(orgHTML, "<li><i><a ", "</a></i></li>")
log (item 0 of readyText)
set removeArray to my extractBetween(readyText, "href", ">")
set completeArray to {}
repeat with rt from 0 to (count readyText)
repeat with ra from 0 to (count removeArray)
if (item ra of removeArray) is in (item rt of readyText) then
set completeName to trim_line((item rt of readyText), (item ra of removeArray), 1)
set end of completeArray to completeName
end if
end repeat
end repeat
log completeArray
end tell
on extractBetween(SearchText, startText, endText)
set tid to AppleScript's text item delimiters -- save them for later.
set AppleScript's text item delimiters to startText -- find the first one.
set liste to text items of SearchText
set AppleScript's text item delimiters to endText -- find the end one.
set extracts to {}
repeat with subText in liste
if subText contains endText then
copy text item 1 of subText to end of extracts
end if
end repeat
set AppleScript's text item delimiters to tid -- back to original values.
return extracts
end extractBetween
on trim_line(this_text, trim_chars, trim_indicator)
-- 0 = beginning, 1 = end, 2 = both
set x to the length of the trim_chars
-- TRIM BEGINNING
if the trim_indicator is in {0, 2} then
repeat while this_text begins with the trim_chars
try
set this_text to characters (x + 1) thru -1 of this_text as string
on error
-- the text contains nothing but the trim characters
return ""
end try
end repeat
end if
-- TRIM ENDING
if the trim_indicator is in {1, 2} then
repeat while this_text ends with the trim_chars
try
set this_text to characters 1 thru -(x + 1) of this_text as string
on error
-- the text contains nothing but the trim characters
return ""
end try
end repeat
end if
return this_text
end trim_line
Not that smooth and not working. Somehow it seems like I can't get the items out of the list, because it doesn't see it as a list item. Can someone help me out?
Cheers
I would recommend a different approach. DL the source, and then just grab the title between tags. The whole script takes under two seconds. Start with:
property baseURL : "http://en.wikipedia.org/wiki/List_of_television_programs_by_name"
set rawHTML to do shell script "curl '" & baseURL & "'"
set preTag to "\" title=\"" -- " title="
set otid to AppleScript's text item delimiters
set AppleScript's text item delimiters to preTag
set rawList to text items of rawHTML
set nameList to {}
repeat with eachLine in rawList
set theOff to offset of ">" in eachLine
set thisName to text 1 thru (theOff - 2) of eachLine
-- add some error checking here to skip the opening non-title hits, and to fine-tune the precise title string
set nameList to nameList & return & thisName
end repeat
set AppleScript's text item delimiters to otid
return nameList
Add a little error checking, and tweak which preTag and postTag fits best.
I suggest you make use of a specialized 3rd-party tool for this task, which can greatly speed things up.
Here's a solution using the multi-platform web-scraping CLI xidel:
A shell command to demonstrate its brevity and speed (takes less than 1 sec. on my system) - extracts all show names from the page:
xidel -e '//*[#id="mw-content-text"]/ul/li/i/a' https://en.wikipedia.org/wiki/List_of_television_programs_by_name
An equivalent AppleScript snippet - be sure to fill in the path to where you place xidel on your system below:
set targetUrl to "https://en.wikipedia.org/wiki/List_of_television_programs_by_name"
set xPathExpr to "//*[#id=\"mw-content-text\"]/ul/li/i/a"
# Fill in the path to `xidel` on your system here:
set xidelPath to "/path/to/xidel"
# Perform scraping and convert result into an AppleScript list.
set showNames to paragraphs of ¬
(do shell script ¬
quoted form of xidelPath & " -e " & quoted form of xPathExpr & " " & ¬
quoted form of targetUrl)
Here's another solution, use javascript to get the names without any AppleScript loop.
The javascript script takes less than one second to get the names.
tell application "Safari"
make new document at end of every document with properties {URL:"http://en.wikipedia.org/wiki/List_of_television_programs_by_name"}
delay 2 -- in seconds; test for your system
set showsWebList to do JavaScript "var a=new Array();var ul=document.getElementById('mw-content-text').querySelectorAll('UL'); for (var i=1;i<ul.length;i++){li=ul[i].querySelectorAll('LI'); for (var j=0; j< li.length; j++){try {var t=li[j].getElementsByTagName('I')[0].getElementsByTagName('A')[0].innerText; a.push(t)} catch(e) {}}} a;" in document 1
end tell
curl/sed/perl solution:
do shell script "curl 'http://en.wikipedia.org/wiki/List_of_television_programs_by_name' | sed -n '/0-9/,/NewPP/p' | sed -n '/^<li/ s/^.*title=.\\([^\"]*\\).*$/\\1/p' | perl -n -mHTML::Entities -e ' ; print HTML::Entities::decode_entities($_);'"
Here another solution using awk using a very simple script. If the line begins with <li><i> then remove html tags (gsub) and then print it. Then by using every paragraph of the return separated output is converted into a list.
set theURL to "http://en.wikipedia.org/wiki/List_of_television_programs_by_name"
every paragraph of (do shell script "curl " & theURL & " | awk '/^\\<li\\>\\<i\\>/{gsub(\"<[^>]*>\", \"\");print}'")

Gvim folding on system verilog keyword pairs

How do I enable folding on system verilog keywords in Gvim ?
For example
function
Code
....
....
endfunction
I would like Gvim to create a fold from function to endfunction. How do I do that ?
Here is a custom foldexpression that should do what you want. It starts a fold on the line following each "function", and ends it on the line preceding each "endfunction", and otherwise inherits the foldlevel of the previous line.
function! VimFunctionFoldExpr()
if getline(v:lnum-1) =~ '^\s*function'
return '>1'
elseif getline(v:lnum+1) =~ '^\s*endfunction'
return '<1'
else
return '='
endif
endfunction
To tell Vim to use this function, set the following:
set foldmethod=expr
set foldexpr=VimFunctionFoldExpr()
You might also want to tweak your foldtext setting so that it respects the intent level. Here is a SE question about how to do that.

Resources