Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to configure the markdown interpreter? #111

Closed
medwatt opened this issue Nov 7, 2021 · 11 comments
Closed

How to configure the markdown interpreter? #111

medwatt opened this issue Nov 7, 2021 · 11 comments
Labels
bug Something isn't working enhancement New feature or request

Comments

@medwatt
Copy link

medwatt commented Nov 7, 2021

I was looking for a way to run code blocks in markdown and came upon this plugin. It didn't run at first. It turns out that was because I was using markdown syntax file that requires the markdown filetype to change to markdown.pandoc. Changing the file type back to markdown works.

My first question is, how do I make sniprun work when the filetype is changed to markdown.pandoc.

My section question is why can't I put the cursor anywhere inside the code block and have the code block evaluated?

@medwatt medwatt added the bug Something isn't working label Nov 7, 2021
@michaelb
Copy link
Owner

michaelb commented Nov 7, 2021

Hi!

About the first thing, that's currently not possible. (The association filetype<=>interpeters is hardcoded, and though you would probably be able to modify sniprun to handle that, it's cumbersome etc...)

Making allowing sniprun to understand that markdown.pandoc == markdown was a feature that crossed my mind but i never implemented it. It shouldn't be too hard though, I'll give it a look soon, and push v1.0.3

About your second question, you can use sniprun inside a code block, but it will only run your visual selection (or the current line if none). There's a doc at

:SnipInfo GFM_original

(Github flavored markdown) that explains the designed behavior

@medwatt
Copy link
Author

medwatt commented Nov 7, 2021

@michaelb, thanks for the reply. I hope you will give executing code blocks in markdown some priority. This is probably one of the features I want yearn from emac's org-mode so much. I find the idea of documenting code with markdown blocks really appealing. Of course, this could be done with comments, but markdown gives you more options.

I have actually written a small script that can run inside a markdown code block, and either display the result in the command line or paste it underneath the code block. At the moment, there's no caching of variables or the displaying the results inline, as you're doing. I'd have to look at your code to see how to do it.

@michaelb
Copy link
Owner

michaelb commented Nov 7, 2021

I hope you will give executing code blocks in markdown some priority.

What do you mean ? executing code bloc in markdown already works

Screenshot_59

What doesn't work for sure is executing code in a markdown.pandoc file, which is what i'll try to fix.

Could you explain what is the issue exactly ? (desired/expected vs actual behavior?)

Nota bene:

no caching of variables

'caching of variables' is only available for REPL-enabled languages (see :SnipInfo),

or the displaying the results inline

There are other display options if you want to :-)

@michaelb michaelb added the enhancement New feature or request label Nov 7, 2021
@michaelb
Copy link
Owner

michaelb commented Nov 7, 2021

v1.0.3 is up, there is an example in the readme (configuration section) that should help you adjust your configuration) :-)

@medwatt
Copy link
Author

medwatt commented Nov 7, 2021

@michaelb, thanks for fixing it. It now works!

I feel there's one small thing left that I wonder if you'd be able to add to the plugin. In org-mode, there's the ability to display the result of the code block in a block underneath the code block you're executing.

For example:

a = 1
b = 2
print(a+b)

Executing the above code block produces the code block below:

: 3

@medwatt medwatt closed this as completed Nov 7, 2021
@michaelb
Copy link
Owner

michaelb commented Nov 7, 2021

there's the ability to display the result of the code block in a block underneath the code block you're executing

I'm not able to do that cleanly enough that i'd be ok to integrate with sniprun. IMO, inserting real text into a buffer is too error-prone

HOWEVER, it may be possible to do it yourself: there are two options:

  • modify sniprun (the Rust code) to add/modify a display option (hard, I don't recommend)

  • use the 'Api' display option to call your own function (in Lua) that do the trick (easy, but hacky)

@medwatt
Copy link
Author

medwatt commented Nov 7, 2021

there's the ability to display the result of the code block in a block underneath the code block you're executing

I'm not able to do that cleanly enough that i'd be ok to integrate with sniprun. IMO, inserting real text into a buffer is too error-prone

HOWEVER, it may be possible to do it yourself: there are two options:

* modify sniprun (the Rust code) to add/modify a display option (hard, I don't recommend)

* use the 'Api' display option to call your own function (in Lua) that do the trick (easy, but hacky)

If it's not too much to ask, can you provide an example of how to capture the output from the display api?

@michaelb
Copy link
Owner

michaelb commented Nov 7, 2021

There is a link in the readme (API in display options) to the file ressources/api.md that contains such an example

@michaelb
Copy link
Owner

michaelb commented Nov 8, 2021

@medwatt I got a mail with some questions about a bug (displaying n-times the result when using the API), but I can't seem to find the corresponding comment here.

Did you delete it ? / did you fix your issue (if so could you share your solution)?

@medwatt
Copy link
Author

medwatt commented Nov 8, 2021

@michaelb, the code I came up with is given below. It seems to work fine. Maybe, you can improve upon it if you see fit.

There are two minor issues I noticed:

  1. I had to make result a script-level variable because I couldn't pass it into the api_listener function.

  2. When I include matplotlib and have a statement such as plt.plot and plt.show, the plot opens in a separate window as expected, but the interpreter starts lagging behind. What I mean by that is, the code that gets run now is the code that I executed in the last instance.

  3. I tried executing different programming languages in the same markdown file. I tried python and bash. For some reason, running bash causes the python interpreter to restart.

local present, sa = pcall(require, 'sniprun.api')
if not present then
    print("Warning: Snipun not installed")
end

local fn = vim.fn
local result = {}
local M = {}

M.setup = {
    parse_pattern = {
        opening = [[\v^\s{-}```\s{-}%(\{\.)?\zs(\w+)]],
        closing = [[\v^\s{-}```]],
    },
    result_block_name = 'RESULTS',
    runners = {
        python = 'python3',
    },
}

-- use language name for language runner
setmetatable(M.setup.runners, {__index = function (_, k) return k end})

local function ParseBlock()

    -- get line number of beginning of code block
    local blk_beg_line_nr = fn.search(M.setup.parse_pattern.opening, 'bnW')
    if blk_beg_line_nr == 0 then
        print('Not in a Markdown code block')
        return
    end

    -- get line number of closing of code block
    local blk_end_line_nr = fn.search(M.setup.parse_pattern.closing, 'nW')
    if blk_end_line_nr == 0 then
        print('Not in a Markdown code block')
        return
    end

    -- check if block is empty
    local src = fn.getline(blk_beg_line_nr + 1, blk_end_line_nr - 1)
    local count = 0
    for i, line in ipairs(src) do
        if not line:find('^[%s]*$') then break end
        count = i
    end
    if count == #src then print("Code block is empty") return end

    -- get indentation level of code block
    local col_num = fn.match(fn.getline(blk_beg_line_nr), '```')

    -- remove indentation
    if col_num > 0 then
        for i, line in ipairs(src) do
            src[i] = line:sub(col_num + 1)
        end
    end

    -- get block language
    local language = fn.matchstr(fn.getline(blk_beg_line_nr), M.setup.parse_pattern.opening)

    result = {
        src       = src,
        language  = language,
        blk_beg_line_nr = blk_beg_line_nr,
        blk_end_line_nr   = blk_end_line_nr,
        col_num   = col_num,
    }

    return result
end

local function InsertResult(message)

    message = fn.split(message, '\n')

    if #message > 0 then

        local save_cursor = fn.getpos('.')

        -- remove existing results if present
        if fn.match(fn.getline(result.blk_end_line_nr + 2), '```' .. M.setup.result_block_name) >= 0 then
            fn.cursor(result.blk_end_line_nr + 3, 0)
            local end_result_block_line = fn.search('```', 'cW')
            if end_result_block_line then
                if fn.getline(end_result_block_line + 1) == '' then
                    fn.deletebufline(fn.bufname('%'), result.blk_end_line_nr + 2, end_result_block_line + 1)
                else
                    fn.deletebufline(fn.bufname('%'), result.blk_end_line_nr + 2, end_result_block_line)
                end
            end
        end

        -- add result in a block
        fn.append(result.blk_end_line_nr, '')
        fn.append(result.blk_end_line_nr + 1, '```' .. M.setup.result_block_name)
        fn.append(result.blk_end_line_nr + 2, message)
        fn.append(result.blk_end_line_nr + 2 + #message, '```')

        -- indent the result block correctly
        local line_indent = fn.indent(result.blk_beg_line_nr)
        if line_indent > 0 then
            local norm_cmd = string.format("%s,%snormal! >>", result.blk_end_line_nr + 1, result.blk_end_line_nr + #message + 3)
            for _=1, math.floor(line_indent / vim.bo.tabstop) do
                vim.cmd(norm_cmd)
            end
        end

        -- restore the cursor position
        fn.setpos('.', save_cursor)
    else
        print("Block ran successfully")
    end

    result = {}
end

function M.RunCodeBlock()
    result = ParseBlock()
    local lang_runner = M.setup.runners[result.language]
    local message = fn.system(lang_runner, result.src)
    InsertResult(message)
end


if present then

    function M.RunCodeBlockSnipRun()
        result = ParseBlock()
        sa.run_range(result.blk_beg_line_nr+1, result.blk_end_line_nr-1, result.language)
    end

    local function api_listener(d)
        if d.status == 'ok' then
            InsertResult(d.message)
        elseif d.status == 'error' then
            print("Error: ", d.message)
        end
    end

    sa.register_listener(api_listener)
end


return M

@michaelb
Copy link
Owner

michaelb commented Nov 8, 2021

Thanks!

When I include matplotlib and have a statement such as plt.plot and plt.show, [the interpreter lags behind]

I noticed that as well for some python interpreters / matplotlib figures, it was quirky enough getting those to work at all😅. Putting plt.show() twice is obviously not a good solution, but maybe using a different python intepreter would solve this issue?
Python3_fifo is my fav', though it has a few drawbacks...

I'll investigate your issue n°3 but I would be surprised if I were able to find the root issue and fix it.
It might be due to the fact that you use the api to run a range directly, but i'm not too sure about that.

Well done anyway!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants