VonHeikemen / lsp-zero.nvim

A starting point to setup some lsp related features in neovim.
https://lsp-zero.netlify.app/v4.x/
MIT License
3.75k stars 95 forks source link

Completion for some languages not working. #273

Closed corei8 closed 1 year ago

corei8 commented 1 year ago

The Issue

Completion is not working for me for Python and Javascript, using the default, minimal configuration as in the docs.

My configuration:
local lsp = require('lsp-zero').preset({})

lsp.on_attach(function(client, bufnr)
  lsp.default_keymaps({buffer = bufnr})
end)

-- (Optional) Configure lua language server for neovim
require('lspconfig').lua_ls.setup(lsp.nvim_lua_ls())

lsp.setup()

I have a much more elaborate config on a different machine (Mac Studio) which works as expected, but I have been forced to use my Intel MacBook Pro for a few weeks. I installed my Nvim Config from GitHub, but completion did not work for Python, as it had on my other machine.

Perhaps it has something to do with my Python configuration? pip -V gives:

pip 23.1.2 from /Users/**********/Library/Python/3.9/lib/python/site-packages/pip (python 3.9)

I have done reinstalls of lsp-zero and all of its dependancies exactly as they are listed in the docs. Requiring the lsp server for Python (jedi and ruff-lsp) has no effect).

The Odd Thing

Lsp autocomplete was working for some time a few days ago -- maybe for 24/48 hours, and then stopped, without there being a change in either the Python installation or the NeoVim configuration.

VonHeikemen commented 1 year ago

You can check the logs for the LSP. Use the command :LspLog, maybe there is something there that can help.

You can even increase the level of detail in the logs by placing this in your config.

vim.lsp.set_log_level("debug")
corei8 commented 1 year ago

These seem to be the relevant logs:

[INFO][2023-06-25 16:50:40] .../vim/lsp/rpc.lua:636 "Starting RPC client"   {  args = {},  cmd = "/Users/**********/.local/share/nvim/mason/bin/ruff-lsp",  extra = {    cwd = "/Users/**********/github/ordo/ordo_tools"  }}
[DEBUG][2023-06-25 16:50:40] .../vim/lsp/rpc.lua:258    "rpc.send"  {  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      general = {        positionEncodings = { "utf-16" }      },      textDocument = {        callHierarchy = {          dynamicRegistration = false        },        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = true,          isPreferredSupport = true,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = true,            deprecatedSupport = true,            documentationFormat = { "markdown", "plaintext" },            insertReplaceSupport = true,            insertTextModeSupport = {              valueSet = { 1, 2 }            },            labelDetailsSupport = true,            preselectSupport = true,            resolveSupport = {              properties = { "documentation", "detail", "additionalTextEdits", "sortText", "filterText", "insertText", "textEdit", "insertTextFormat", "insertTextMode" }            },            snippetSupport = true,            tagSupport = {              valueSet = { 1 }            }          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          completionList = {            itemDefaults = { "commitCharacters", "editRange", "insertTextFormat", "insertTextMode", "data" }          },          contextSupport = true,          dynamicRegistration = false,          insertTextMode = 1        },        declaration = {          linkSupport = true        },        definition = {          dynamicRegistration = true,          linkSupport = true        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        formatting = {          dynamicRegistration = true        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = true        },        implementation = {          linkSupport = true        },        inlayHint = {          dynamicRegistration = false,          resolveSupport = {            properties = {}          }        },        publishDiagnostics = {          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        rangeFormatting = {          dynamicRegistration = true        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = true,          prepareSupport = true        },        semanticTokens = {          augmentsSyntaxTokens = true,          dynamicRegistration = false,          formats = { "relative" },          multilineTokenSupport = false,          overlappingTokenSupport = true,          requests = {            full = {              delta = true            },            range = false          },          serverCancelSupport = false,          tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },          tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = true,          willSaveWaitUntil = true        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = true        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        didChangeWatchedFiles = {          dynamicRegistration = true,          relativePatternSupport = true        },        inlayHint = {          refreshSupport = true        },        semanticTokens = {          refreshSupport = true        },        symbol = {          dynamicRegistration = false,          hierarchicalWorkspaceSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.10.0"    },    initializationOptions = vim.empty_dict(),    processId = 39488,    rootPath = vim.NIL,    rootUri = vim.NIL,    trace = "off",    workspaceFolders = vim.NIL  }}
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Workspace settings: null",    type = 4  }}
[INFO][2023-06-25 16:50:41] ...lsp/handlers.lua:544 "Workspace settings: null"
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Global settings: {}",    type = 4  }}
[INFO][2023-06-25 16:50:41] ...lsp/handlers.lua:544 "Global settings: {}"
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      codeActionProvider = {        codeActionKinds = { "quickfix", "source.fixAll", "source.organizeImports", "source.fixAll.ruff", "source.organizeImports.ruff" },        resolveProvider = true      },      executeCommandProvider = {        commands = { "ruff.applyAutofix", "ruff.applyOrganizeImports" }      },      hoverProvider = true,      textDocumentSync = {        change = 2,        openClose = true,        save = true,        willSave = false,        willSaveWaitUntil = false      },      workspace = {        fileOperations = vim.empty_dict(),        workspaceFolders = {          changeNotifications = true,          supported = true        }      }    },    serverInfo = {      name = "Ruff",      version = "0.0.35"    }  }}
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:258    "rpc.send"  {  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[INFO][2023-06-25 16:50:41] .../lua/vim/lsp.lua:1459    "LSP[ruff_lsp]" "server_capabilities"   {  server_capabilities = {    codeActionProvider = {      codeActionKinds = { "quickfix", "source.fixAll", "source.organizeImports", "source.fixAll.ruff", "source.organizeImports.ruff" },      resolveProvider = true    },    executeCommandProvider = {      commands = { "ruff.applyAutofix", "ruff.applyOrganizeImports" }    },    hoverProvider = true,    textDocumentSync = {      change = 2,      openClose = true,      save = true,      willSave = false,      willSaveWaitUntil = false    },    workspace = {      fileOperations = vim.empty_dict(),      workspaceFolders = {        changeNotifications = true,        supported = true      }    }  }}
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:258    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "python",      text = "import subprocess\nfrom datetime import datetime\nimport os\nimport importlib\nfrom ordo_tools.feast import Feast\nfrom ordo_tools.helpers import latex_replacement, translate_month\n\n\ndef build_latex_ordo(year):\n    \"\"\" build an ordo booklet in 8.5 by 5.5 \"\"\"\n    try:\n        mdl = importlib.import_module('calen.calendar_' + str(year)).calen\n    except AttributeError:\n        pass\n    else:\n        mdldates = sorted(mdl)\n        with open(\"output/latex/ordo_\" + str(year) + \".tex\", \"a\") as f:\n            f.truncate(0)\n            f.write(\n                r'''\n    % !TEX program = lualatex\n    \\documentclass[10pt, openany]{book}\n    \\title{Ordo '''+str(year)+r'''}\n    \\author{Roman Catholic Institute}\n    \\usepackage{ragged2e}\n    \\usepackage{gregoriosyms} % for the versicle and response symbols\n    \\usepackage{microtype}\n    \\usepackage[T1]{fontenc}\n    \\usepackage{fontspec}\n    \\setmainfont[\n        Path = /Library/Fonts/, \n        Extension = .ttf, \n        Ligatures = TeX, \n        BoldFont = Cardob101, \n        ItalicFont = Cardoi99,\n        ]{Cardo104s}\n    \\usepackage[latin]{babel}\n    \\usepackage{geometry}\n    \\geometry{\n        paperheight=8.5in, \n        paperwidth=5.5in, \n        left=1.0in, \n        right=1.0in, \n        top=1.0in, \n        bottom=1.0in,\n        }\n    \\usepackage{anyfontsize}\n    \\usepackage{fancyhdr}\n    \\pagestyle{fancy}\n    \\renewcommand{\\chaptermark}[1]{% \n        \\markboth{#1}{}\n        }\n    \\begin{document}\n        % \\maketitle\n        % just something temporary for now\n        \\begin{titlepage}\n            \\begin{center}\n                {\\fontsize{50}{60}\\selectfont \\textsc{Ordo '''+str(year)+r'''}}\n            \\end{center}\n            \\begin{center}\n                {\\footnotesize \\textsc{Roman Catholic Institute}}\n            \\end{center}\n        \\end{titlepage}\n        \\clearpage\\begingroup\\pagestyle{empty}\\cleardoublepage\\endgroup\n    '''\n            )\n            for i in range(1, 13):\n                month = datetime.strptime(\n                    str(i)+'/1/'+str(year), '%m/%d/%Y').strftime('%B')\n                _month = translate_month(month)\n                # todo get rid of the chapter number\n                f.write(\n                    r'''\n        \\chapter{''' + _month + r'''}\n                        ''')\n                for x in mdldates:\n                    if int(x.split('/')[0]) == i:\n                        feast = Feast(x, mdl[x])\n                        # todo make the header of the last page of the previous month match the previous month\n                        f.write(\n                            r'''\n        \\begin{center}\n            \\begin{minipage}{3.5in}\n                \\vspace{2em}\n                \\begin{minipage}{0.5in}\n                    {\\Huge '''+latex_replacement(feast.feast_date_display)+r'''} \\\\\n                    {\\normalsize '''+feast.translate_weekday+r'''} \\\\\n                    {\\normalsize '''+feast.translate_color+r'''}\n                \\end{minipage}\n                \\begin{minipage}{3.0in}\n                    \\textbf{ \\large '''+latex_replacement(feast.name)+r''' \\\\\n                    \\textnormal{\\normalsize '''+feast.rank_v+r'''}} \\\\ ''' + latex_replacement(feast.com_in_title)+r'''\n                \\end{minipage}\n                \\begin{justify}'''+feast.office_type2latex+\n                    feast.display_matins_as_latex + \n                    feast.display_lauds_as_latex +\n                    feast.display_little_hours_as_latex +\n                    feast.display_prime_as_latex +\n                    latex_replacement(feast.display_mass_as_latex())+\n                    feast.display_vespers_as_latex + \n                    feast.display_compline_as_latex +r'''\n                \\end{justify}\n            \\end{minipage}\n        \\end{center}\n    ''')\n            f.write(\"\\n\\end{document}\")\n        file = 'ordo_'+str(year)+'.tex'\n        working_dir = os.getcwd()\n        os.chdir('output/latex/')\n        subprocess.run(\n            'lualatex '+file+' -interaction nonstopmode',\n            shell=True ,\n            stdout=subprocess.DEVNULL\n        )\n        # TODO: move the pdf into a seperate directory and overwrite the old one\n        os.chdir(working_dir)\n    return None\n\n\ndef readme_calendar(year, mdl: dict):\n    try:\n        mdl = importlib.import_module(\n            'calen.calendar_' + str(year)).calen\n    except AttributeError:\n        pass\n    else:\n        mdldates = sorted(mdl)\n        with open('README.md', 'a') as f:\n            f.truncate(0)\n            f.write(\n                r\"\"\"\n# Ordo\n\nTraditional Catholic Ordo for the United States, Australia, Canada and Nantes.\n\nManaged by the Roman Catholic Institute.\n\n## Python Specifications\n\nPython 3.x.x 64-bit\n\n### Modules:\n\n[dateutil](https://dateutil.readthedocs.io/en/stable/)\n```bash\npip install python-dateutil\n```\n\n## Overview\n\nThe temporal cycle is generated by a funtion as a dictionary. The sanctoral cycle is made up of several files for country, region and diocese, and each file is a pre-built dictionary. These are combined with the temporal cycle after the latter is generated.\n\nThe result is a dictionary containing:\n\n1. A liturgical calendar proper to an specified diocese (or several dioceses);\n2. Indications of the peculiarities of the office of that day;\n3. An ordo for the Mass of that day, or multiple Masses, if applicable.\n\nEaster is the first feast (every 'event' is treated as a feast) to be determined, since most of the liturgical year depends upon the date of Easter. Christmas, being static with regard to its date, requires that we only find the day of the week on which it falls. We begin building the temporal calendar with 01-01.\n\n## Progress\n\n- [x] Temporal Calendar\n- [x] Combined Temporal and Sanctoral Calendar\n- [ ] Masses\n- [ ] Vespers\n- [x] Colors of Mass and Office\n- [ ] Lessons for Laudes\n- [ ] Prime\n- [ ] Little Hours\n- [ ] US Calendar\n- [ ] Australian Calendar\n- [ ] Canadian Calendar\n- [ ] Solemnities\n                    \"\"\")\n            f.write('\\n\\n')\n            f.write('## Calendar for ' + str(year))\n            f.write('\\n\\n')\n            f.write('| Day | Date | Feast |')\n            f.write('\\n')\n            f.write('|---|---|---|')\n            f.write('\\n')\n            for x in mdldates:\n                feast = Feast(x, mdl[x])\n                f.write('| ' + feast.translate_weekday + ' | ' + feast.feast_date + ' | ' + feast.name + ' |')\n                f.write('\\n')\n    return None\n\n\ndef build_latin_calendar(year) -> None:\n    # todo make this calendar import work with a variable\n    try:\n        mdl = importlib.import_module('calen.calendar_' + str(year)).calen\n    except AttributeError:\n        pass\n    else:\n        mdldates = sorted(mdl)\n        with open('output/latex_calendar/calendar_'+str(year)+'.tex', 'a') as f:\n            f.truncate(0)\n            f.write(\n                r\"\"\"\n    % !TEX program = lualatex\n    \\documentclass[10pt]{article}\n    \\usepackage{calendar_letter}\n    \\usepackage[landscape, letterpaper, margin=.25in]{geometry}\n    \\usepackage{palatino}\n    \\begin{document}\n    \\pagestyle{empty}\n    \\setlength{\\parindent}{0pt}\n    \\StartingDayNumber=1\n    \"\"\"\n            )\n            for i in range(1, 13):\n                blank_days = datetime.strptime(\n                    str(i)+'/1/'+str(year), '%m/%d/%Y').strftime('%w')\n                month = datetime.strptime(\n                    str(i)+'/1/'+str(year), '%m/%d/%Y').strftime('%B')\n                f.write(\n                    r'''\n    \\begin{center}\n        \\textsc{\\LARGE '''+month+r'''}\\\\ % Month\n        % \\textsc{\\large Year} \\\\ % Year\n    \\end{center}\n    \\begin{calendar}{\\textwidth}\n    '''\n                )\n                for x in range(int(blank_days)):\n                    f.write(\n                        r'''\n    \\BlankDay\n    '''\n                    )\n                f.write(r'''\n    \\setcounter{calendardate}{1}\n    '''\n                        )\n                for x in mdldates:\n                    if int(x.split('/')[0]) == i:\n                        feast = Feast(x, mdl[x])\n                        f.write(\n                            r'''\n    \\day{'''+latex_replacement(feast.name)+r'''}{\\vspace{1.5cm}}\n    '''\n                        )\n                f.write(\n                    r'''\n    \\finishCalendar\n    \\end{calendar}\n    \\pagebreak\n    '''\n                )\n            f.write(\n                r'''\n    \\end{document}\n                '''\n            )\n        file = 'calendar_'+str(year)+'.tex'\n        working_dir = os.getcwd()\n        os.chdir('output/latex_calendar/')\n        subprocess.run('lualatex '+file+' -interaction nonstopmode', shell=True)\n        os.chdir(working_dir)\n    return None\n\n\ndef build_test_website(year: int, y: dict) -> None:\n\n    # build the calendar with blank days\n    cal = []\n    for x in range(54):\n        cal.append([])\n        for d in range(7):\n            cal[x].append([])\n\n    # %U' is the week number with Sunday being the first day of the week\n    # %w' is the weekday number with Sunday as the first day of the week\n\n    # Add the data for each day at the beginning\n    for date in y.keys():\n        placement = int(date.strftime('%U'))\n        cal[placement][int(date.strftime('%w'))] = [\n            y[date],\n            date.strftime(\"%B\"),\n            date.strftime(\"%d\")\n        ]\n\n    # determine the file output and starter text depending on the calendar\n    build_dirs = [\n        \"./output/ordosite/_includes/calendar.html\", # for the Jekyll site\n        \"./output/html/index.html\"                   # for quick reference\n    ]\n    last_week  = False\n\n    for out, path in enumerate(build_dirs):\n        with open(path, 'w') as f:\n            f.truncate(0)\n            if out == 1:\n                f.write(\"\"\"\n                <!DOCTYPE html>\n                <html lang=\"\"en-us\">\n                <head>\n                <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n                <meta charset=\"utf-8\">\n                <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM\" crossorigin=\"anonymous\">\n                <style>body {background: aliceblue;}</style>\n                <title>test site</title>\n                </head>\n                <body>\n                \"\"\")\n            f.write('<div class=\"container center p-0\">') # TODO: see if we can get rid of this\n\n            # useful variables\n            month_memory = ''\n            weekdays = \"\"\"\n    <div class=\"row w-100 m-0 rounded-top bg-primary\">\n    <div class=\"col bg-primary text-white p-1 text-center rounded\"> Sunday </div>\n    <div class=\"col bg-primary text-white p-1 text-center\"> Monday </div>\n    <div class=\"col bg-primary text-white p-1 text-center\"> Tuesday </div>\n    <div class=\"col bg-primary text-white p-1 text-center\"> Wednesday </div>\n    <div class=\"col bg-primary text-white p-1 text-center\"> Thursday </div>\n    <div class=\"col bg-primary text-white p-1 text-center\"> Friday </div>\n    <div class=\"col bg-primary text-white p-1 text-center rounded\"> Saturday </div>\n    </div>\n            \"\"\"\n\n            def close_div():\n                return '</div>'\n\n            def start_row(classes=''):\n                return f'<div class=\"row w-100 m-0 {classes}\">'\n\n            def start_col(classes=''):\n                return f'<div class=\"col p-1 text-break {classes}\"\\\n                style=\"min-height: 10em;\">'\n\n            def empty_col(classes=''):\n                return start_col(classes)+close_div()\n\n            def build_month(month, cols, file, year=year) -> None:\n                file.write('<section>')\n                file.write(start_row()) # starts the month row\n                    # <h1 class=\"display-6 pt-3\">\n                file.write(f'''\n                <div class=\"mt-3 text-start\">\n                    <h3 class=\"pt-1\">\n                        {month} {year}\n                    </h3>\n                </div>\n                ''')\n                file.write(close_div()) # closes the month row\n                file.write(f'{weekdays}')\n                file.write(start_row('border empty_dates'))\n                file.write(empty_col()*cols)\n                return None\n\n            for j, aweek in enumerate(cal):\n\n                \n                # See if it is the last week of the year\n                if last_week is True:\n                    break\n                elif j+1 == len(cal):\n                    last_week = True\n                else:\n                    pass\n\n                for i, aday in enumerate(aweek):\n\n                    # alternate the cell shading\n                    if i%2 == j%2:\n                        shading = 'bg-body'\n                    else:\n                        shading = 'bg-light'\n\n                    # see if the day is a calendar day\n                    if len(aday) == 2:\n                        index = 0 # the day is not a calendar day\n                    elif len(aday) == 0:\n                        # f.write(empty_col())\n                        continue\n                    else:\n                        index = 1\n\n                    if aday[index] != month_memory: # if we have a new month\n                        if j == 1 and i != 0:\n                            pass\n                        elif j == 1 and i == 0:\n                            pass\n                        else:\n                            if j == 0:\n                                f.write('<section>')\n                            else:\n                                f.write(empty_col()*int(7-i))\n                                f.write(close_div())\n                                f.write('</section>')\n                        build_month(month=aday[index], cols=i, file=f)\n                    else:\n                        if i == 0 and j != 0:\n                            f.write(start_row('border empty_dates'))\n\n                    month_memory = aday[index]\n\n                    f.write(start_col(\n                        f'fw-light d-flex flex-column justify-content-between {shading}'\n                    ))\n\n                    # date-bar\n                    f.write(f'<div class=\"w-100 p-1\">{aday[-1].lstrip(\"0\")}</div>')\n\n                    # feast\n                    print(f\" adding -> {aday[0]['feast']}\")\n                    f.write(f'''\n                    <div class=\"text-center smaller-text w-100\">\n                    {'<h1>🧐</h1>' if index != 1 else aday[0]['feast']}\n                    </div>\n                    ''')\n\n                    # statusbar helpers\n                    if out == 1:\n                        fish_path = \"assets/images/full_fish.png\"\n                    else:\n                        fish_path = \"/assets/images/full_fish.png\"\n                    color = aday[0][\"color\"]\n                    blank_image = \"data:image/gif;base64,R0lGODlhAQABAIAAAP///\\\n                    wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==\"\n                    fish_placeholder = f'<img src=\"\\\n                    {blank_image}\\\n                    \" height=\"16em\" width=\"16em\">'\n\n                    # build the \"statusbar\"\n                    f.write(f'''\n                    <div class=\"w-100 p-0 d-flex flex-column justify-content-between align-items-center\">\n                    \n                    { f'<div><small>{aday[0][\"rank\"][1]}</small></div>'}\n                    <div class=\"text-end w-100 p-1 d-flex flex-row\n                    justify-content-between align-items-end\" height=\"16em\">\n                    \n                    { f'<img src=\"{blank_image}\" height=\"16em\" width=\"16em\" style=\"border: solid 1px black; border-radius: 50%; background: {color}\">'}\n                    \n                    {f'<img src=\"{fish_path}\" height=\"16em\">' if aday[0][\"fasting\"] is True or i == 5 else fish_placeholder}\n\n                    </div></div>\n                    ''')\n\n                    f.write(\"</div>\") # close the column\n\n                    # add blank days if it is the last day\n                    if last_week is True and aday[-1] == str(31):\n                        f.write(empty_col()*int(6-i))\n                        break\n\n                f.write(\"</div>\") # close the row\n\n            if out == 1:\n                f.write(\"</section></div></div></body></html>\")\n            # f.write(\"</div>\")\n\n            f.close() # FIX: might not be necessary\n\n        with open(path) as f:\n            lines = list(f)\n        with open(path, 'w') as out:\n            for line in lines:\n                out.write(line.lstrip())\n            f.close()\n\n    return None\n\n",      uri = "file:///Users/**********/github/ordo/ordo_tools/outputs.py",      version = 0    }  }}
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Using interpreter executable: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff",    type = 4  }}
[INFO][2023-06-25 16:50:41] ...lsp/handlers.lua:544 "Using interpreter executable: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff"
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Running Ruff with: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--format', 'json', '-', '--stdin-filename', '/Users/**********/github/ordo/ordo_tools/outputs.py']",    type = 4  }}
[INFO][2023-06-25 16:50:41] ...lsp/handlers.lua:544 "Running Ruff with: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--format', 'json', '-', '--stdin-filename', '/Users/**********/github/ordo/ordo_tools/outputs.py']"
[DEBUG][2023-06-25 16:50:41] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = { {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 79        },        message = "Line too long (110 > 88 characters)",        range = {          ["end"] = {            character = 110,            line = 78          },          start = {            character = 88,            line = 78          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 92        },        message = "Line too long (119 > 88 characters)",        range = {          ["end"] = {            character = 119,            line = 91          },          start = {            character = 88,            line = 91          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 175        },        message = "Line too long (257 > 88 characters)",        range = {          ["end"] = {            character = 257,            line = 150          },          start = {            character = 88,            line = 150          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 175        },        message = "Line too long (315 > 88 characters)",        range = {          ["end"] = {            character = 315,            line = 158          },          start = {            character = 88,            line = 158          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 185        },        message = "Line too long (110 > 88 characters)",        range = {          ["end"] = {            character = 110,            line = 184          },          start = {            character = 88,            line = 184          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 308        },        message = "Line too long (226 > 88 characters)",        range = {          ["end"] = {            character = 226,            line = 302          },          start = {            character = 88,            line = 302          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 309        },        message = "Line too long (95 > 88 characters)",        range = {          ["end"] = {            character = 95,            line = 308          },          start = {            character = 88,            line = 308          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 442        },        message = "Line too long (105 > 88 characters)",        range = {          ["end"] = {            character = 105,            line = 430          },          start = {            character = 88,            line = 430          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 442        },        message = "Line too long (151 > 88 characters)",        range = {          ["end"] = {            character = 151,            line = 436          },          start = {            character = 88,            line = 436          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 442        },        message = "Line too long (124 > 88 characters)",        range = {          ["end"] = {            character = 124,            line = 438          },          start = {            character = 88,            line = 438          }        },        severity = 2,        source = "Ruff"      } },    uri = "file:///Users/**********/github/ordo/ordo_tools/outputs.py"  }}
[DEBUG][2023-06-25 16:53:24] .../vim/lsp/rpc.lua:258    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "python",      text = "from datetime import date\nfrom datetime import datetime\nfrom datetime import timedelta\n\nfrom ordo_tools.helpers import days\nfrom ordo_tools.helpers import advance_a_day\nfrom ordo_tools.helpers import leap_year\nfrom ordo_tools.helpers import findsunday\nfrom ordo_tools.helpers import LENT_ENDS\nfrom ordo_tools.helpers import LENT_BEGINS\nfrom ordo_tools.helpers import LAST_ADVENT\nfrom ordo_tools.helpers import FIRST_ADVENT\n\n\nfrom ordo_tools.feast import Feast\nfrom ordo_tools.settings import YEAR\nfrom ordo_tools.temporal import Temporal\nfrom sanctoral.diocese.roman import Sanctoral\n\nfrom os import listdir\n\nimport importlib\n\nfrom ordo_tools.parts import ROMANS\n\n\n\n\n# def mart_letter(year) -> str:\n#     \"\"\" Find the letter of the martyrology for a given year \"\"\"\n#     golden_number = (year + 1) % 19\n#     if golden_number == 0:  # ? is this true?\n#         golden_number = 19\n#     return golden_number\n\n\n# MARTYROLOGY = (lambda year: mart_letter(year))(YEAR)\n\n\ndef find_module(name: str) -> tuple:\n    \"\"\"\n    Finds the module and the function within the module.\n    \"\"\"\n    if '.' in name:\n        name = name.split('.')[1].strip('_')\n    if name + '.py' in listdir('sanctoral/diocese'):\n        file_name = 'sanctoral/diocese/' + name + '.py'\n        mdl_name = 'sanctoral.diocese.' + name\n        dict_name = 'sanctoral'\n        dict_name_json = 'sanctoral = {'\n    elif name == 'temporal':\n        file_name = 'temporal/' + name + '_' + str(YEAR) + '.py'\n        mdl_name = 'temporal.' + name + '_' + str(YEAR)\n        dict_name = 'temporal'\n        dict_name_json = 'temporal = {'\n    elif name == 'calendar':\n        file_name = 'calen/' + name + '_' + str(YEAR) + '.py'\n        mdl_name = 'calen.' + name + '_' + str(YEAR)\n        dict_name = 'calen'\n        dict_name_json = 'calen = {'\n    else:\n        file_name = ''\n        mdl_name = ''\n        dict_name = ''\n        dict_name_json = ''\n    return (file_name, mdl_name, dict_name, dict_name_json)\n\n# TODO: maybe use a decorator to make this more readable?\n\n\ndef commit_to_dictionary(target_file: str, dic: dict) -> None:\n    \"\"\"\n    Takes a dictionary and writes it to a file.\n    \"\"\"\n\n    def update_calendar(data: dict) -> dict:\n        \"\"\"\n        Updates the calendar file\n        \"\"\"\n        cal = {}\n        for x, y in data.items():\n            cal.update({x: y})\n        with open('calen/calendar_' + str(YEAR) + '.py', 'a') as f:\n            f.truncate(0)\n            f.write('from ordo_tools.helpers import day\\n\\n')\n            f.write(\"class LiturgicalCalendar:\\n\\n\")\n            f.write(\"\\tdef __init__(self):\\n\")\n            f.write('\\t\\tself.data = {\\n')\n            for i, line in enumerate(sorted(cal)):\n                date = f\"\"\"\\t\\t\\tday(year={YEAR}, month={line.strftime('%m').lstrip('0')}, day={line.strftime('%d').lstrip('0')})\"\"\"\n                if i != 0:\n                    f.write(f\"{date}: {cal[line]},\\n\")\n                else:\n                    f.write(f\"{date}: {cal[line]},\\n\")\n            f.write('\\t\\t}\\n\\n')\n            f.write('\\t\\tdef data(self) -> dict:\\n')\n            f.write('\\t\\t\\treturn self.data')\n\n        return None\n\n    # update_calendar(data=dic)\n\n    return dic\n\n\ndef explode_octaves(region_diocese: str) -> dict:\n    # TODO: reimplement this to have a seperate dictionary for these\n    \"\"\" Takes the Octaves in the Sanctoral cycle and explodes them into\n    their days within the octave.\"\"\"\n    mdl = importlib.import_module(\n        'sanctoral.diocese.' + region_diocese).sanctoral\n    return_dict = {}\n    for x in sorted(mdl):\n        feast = Feast(x, mdl[x])\n        if 'Oct' in feast.rank_v:\n            if feast.nobility[2] == 4:  # common octave\n                # todo update this to handle every octave type\n                # * this is an ok use of ROMANS\n                for k, y in enumerate(ROMANS[3:6], start=1):\n                    feast.name = 'De ' + y + ' die infra '\n                    + feast.infra_octave_name\n                    feast.rank_v = 'feria'\n                    feast.rank_n = 18\n                    return_dict.update(\n                        {\n                            str((datetime.strptime(feast.feast_date, '%m/%d')\n                                + days(k)).strftime('%m/%d')) + '_':\n                            feast.updated_properties\n                        }\n                    )\n        else:\n            return_dict.update({feast.feast_date: feast.feast_properties})\n    return return_dict\n\n\ndef add_commemoration(feast: Feast, commemoration: Feast) -> dict:\n    \"\"\"\n    Adds one feast as the commemoration of another feast.\n    Accepts feast properties.\n    \"\"\"\n    feast.com.insert(0, commemoration.feast_properties)\n    return feast.updated_properties\n\ndef rank_by_nobility(feast_1: Feast, feast_2: Feast) -> dict:\n    \"\"\" Takes two feasts and returns the one with the higher rank.\"\"\"\n    ranks_1 = feast_1.nobility\n    ranks_2 = feast_2.nobility\n    for x in range(len(ranks_1)):\n        if ranks_1[x] < ranks_2[x]:\n            return {'higher': feast_1, 'lower': feast_2}\n        elif ranks_1[x] > ranks_2[x]:\n            return {'higher': feast_2, 'lower': feast_1}\n        else:\n            pass\n    return {'higher': feast_1, 'lower': feast_2}\n\n\ntransfers = {}\n\n# * TRY to use ranges for special times: regualar, epiphnay, etc.\n\n\ndef rank_occurring_feasts(\n    date: str,\n    sanctoral_feast: dict,\n    temporal_feast: dict\n    ) -> dict:\n    \"\"\"\n    Ranks concurring feasts.\n    \"\"\"\n    ranked_feasts = {}\n    transfers = {}\n    sanct = Feast(date, sanctoral_feast)\n    tempo = Feast(date, temporal_feast)\n    # TODO: refactor\n    if sanct.rank_n == tempo.rank_n: # FIX: this is not working\n        transfer_date = date+timedelta(days=1)\n        ranked_feasts |= {\n            date: rank_by_nobility(sanct, tempo)['higher'].feast_properties,\n            transfer_date: rank_by_nobility(sanct, tempo)['lower'].feast_properties,\n        }\n    else:\n        candidates = {\n            sanct.rank_n: sanct,\n            tempo.rank_n: tempo,\n        }\n        higher = candidates[sorted(candidates)[0]]\n        lower = candidates[sorted(candidates)[1]]\n        if lower == 22:         # take care of simple feasts\n            pass\n        if higher.rank_n <= 4:  # feasts that exclude commemorations\n            if lower.rank_n <= 10:\n                ranked_feasts.update({date: higher.feast_properties})\n                transfers |= {date: lower.feast_properties}\n            else:\n                ranked_feasts.update({date: higher.feast_properties})\n        elif 14 <= lower.rank_n <= 16:    # impeded dm, d and sd\n            if higher.rank_n == 12 or 19: # see p. 309 Matters Liturgical\n                ranked_feasts |= {\n                    date: add_commemoration(feast=higher, commemoration=lower)\n                }\n            else:\n                ranked_feasts |= {\n                    date: add_commemoration(feast=higher, commemoration=lower)\n                }\n        else:\n            ranked_feasts |= {\n                date: add_commemoration(feast=higher, commemoration=lower)\n            }\n    return ranked_feasts\n\n\ndef add_sanctoral_feasts(temporal_dict: dict, sanctoral_dict: dict) -> dict:\n    \"\"\"\n    Adds the sanctoral feasts to the temporal feast dictionary.\n    \"\"\"\n    # TODO: see if copying is necessary:\n    sanctoral = sanctoral_dict.copy()\n    temporal = temporal_dict.copy()\n    full_calendar = {}\n    for the_date in sanctoral.keys():\n        if the_date in temporal.keys():\n            ranked_feast = rank_occurring_feasts(\n                date=the_date,\n                sanctoral_feast=sanctoral[the_date],\n                temporal_feast=temporal[the_date]\n            )\n            full_calendar |= ranked_feast\n        else:\n            full_calendar |= {date: sanctoral[the_date]}\n    return full_calendar\n\n\ndef dict_clean(direct: str, str_flag: str) -> None:\n    \"\"\"\n    Resolves conflicts between the sanctoral and temporal dictionaries,\n    which are flagged with either a period or an underscore.\n    \"\"\"\n    dict_information = find_module(direct)\n    dictionary = importlib.import_module(dict_information[1])\n    try:\n        dic = dictionary.__dict__[dict_information[2]]\n    except KeyError:\n        pass\n    else:\n        flag = str_flag\n        for x in sorted(dic):\n            if flag in x:\n                continue\n            if x + flag in sorted(dic):\n                rank_occurring_feasts(\n                    date=x,\n                    sanctoral_feast=dic[x],\n                    temporal_feast=dic[x + flag]\n                )\n        else:\n            commit_to_dictionary(target_file=direct, dic=dic)\n    return 0\n\n\ndef dict_clean_mini(direct: str, str_flag: str) -> None:\n    \"\"\"\n    Resolves conflicts in a single file, which are flagged with either\n    a period or an underscore.\n    \"\"\"\n    dict_information = find_module(direct)\n    dictionary = importlib.import_module(dict_information[1])\n    dic = dictionary.__dict__[dict_information[2]]\n    flag = str_flag\n    list_of_keys = [k for k in dic.keys()]\n    for x in list_of_keys:\n        if flag in x:\n            continue\n        if x + flag in dic.keys():\n            new_rank = rank_occurring_feasts(\n                date=x, sanctoral_feast=dic[x], temporal_feast=dic[x + flag]\n            )\n            dic.update(new_rank)\n            del dic[x + flag]\n    else:\n        return dic\n\n\ndef leap_year_solver(dic: dict) -> dict:\n    \"\"\" Solves the leap year problem. \"\"\"\n    return dic\n\n\ndef transfer_feasts(dic: dict) -> dict:\n    \"\"\" Solves the transfer feast problem. \"\"\"\n    for x in transfers.keys():\n        trans_feast = Feast(x, transfers[x])\n        # look one day ahead\n        target_date = advance_a_day(trans_feast.date)\n        if target_date not in dic.keys():\n            dic.update(target_date, trans_feast.feast_properties)\n        else:\n            # ! apply the ranking rules\n            pass\n    return dic\n\n\n# TODO: this might be a real piece of work to try to salvage\n\ndef stitch_calendars(diocese='roman') -> None:\n    \"\"\" Stitches the temporal and sanctoral calendars together. \"\"\"\n\n    temporal = Temporal(YEAR).return_temporal()\n\n    if diocese == 'roman': # TODO: add another diocese\n        sanctoral = Sanctoral(YEAR).data if leap_year(YEAR) is False else Sanctoral(YEAR).leapyear()\n    else:\n        pass # this will be a headache\n    full_calendar = add_sanctoral_feasts(temporal, sanctoral).copy()\n\n    # TODO: see what is happening here...\n    for y in temporal.keys():\n        if y in sanctoral.keys():\n            continue\n        else:\n            full_calendar.update({y: temporal[y]})\n\n    if len(transfers) > 0:\n        pass\n        # full_calendar = transfer_feasts(full_calendar)\n    else:\n        pass\n\n    return commit_to_dictionary(\n        target_file='calendar',\n        dic=full_calendar,\n    )\n\n\ndef our_ladys_saturday(direct: str) -> None:\n    \"\"\" Adds all the Saturday Offices of the Blessed Virgin\n     to the temporal calendar \"\"\"\n    # todo add mass number according to season\n    office = {\n        'feast': 'De Sancta Maria in Sabbato',\n        'rank': [21, 's'],\n        'color': 'white',\n        'mass': {\n            'int': 'Salve sancta parens',\n            'glo': True,\n            'cre': False,\n            'pre': 'de B Maria Virg (et te in Veneratione)'\n        },\n        'com_1': {'oration': 'Deus qui corda', },\n        'com_2': {'oration': 'Ecclesiæ', },\n        'matins': {},\n        'lauds': {},\n        'prime': {'responsory': 'Qui natus est', 'preces': True},\n        'little_hours': {},\n        'vespers': {\n            'proper': False,\n            'admag': ('firstVespers', 'secondVerspers'),\n            'propers': {},\n            'oration': ''\n        },\n        'compline': {},\n        'office_type': 'ut in pr loco',\n        'nobility': (8, 2, 6, 13, 3, 0,),\n    }\n    dict_information = find_module(direct)\n    dictionary = importlib.import_module(dict_information[1])\n    dic = dictionary.__dict__[dict_information[2]]\n    begin_year = date(YEAR, 1, 1)\n    if begin_year.strftime('%A') == 'Saturday':\n        saturday = begin_year\n    else:\n        saturday = begin_year - days(findsunday(begin_year) + 6)\n    while saturday <= date(YEAR, 12, 31):\n        if LENT_BEGINS.date() <= saturday <= LENT_ENDS.date():\n            saturday += days(7)\n            continue\n        elif FIRST_ADVENT.date() <= saturday <= LAST_ADVENT.date():\n            saturday += days(7)\n            continue\n        # todo #19 #18 also Ember Days, Vigils\n        else:\n            try:\n                saturday_in_temporal = Feast(feast_date=saturday.strftime(\n                    '%m%d'), properties=dic[saturday.strftime('%m/%d')])\n            except KeyError:\n                if saturday.strftime('%m/%d') + '.' in sorted(dic):\n                    pass  # we can presume that it is impossible\n                else:\n                    dic.update({saturday.strftime('%m/%d'): office})\n            else:\n                dic.update(\n                    rank_occurring_feasts(\n                        date=saturday.strftime('%m/%d'),\n                        sanctoral_feast=office,\n                        temporal_feast=saturday_in_temporal.feast_properties\n                    )\n                )\n            saturday = saturday + days(7)\n    commit_to_dictionary(target_file='calendar', dic=dic)\n    return None\n\n\ndef commit_temporal() -> None:\n    \"\"\"\n    Commits the temporal calendar to the temporal dictionary.\n    \"\"\"\n    from temporal_cycle import build_temporal\n    cycle = build_temporal(YEAR)\n    gen_file = \"temporal/temporal_\" + str(YEAR)\n    with open(gen_file + \".py\", \"w\") as f:\n        f.write(\"temporal = {\")\n        memory = []\n        for k, v in cycle.items():\n            temporal_event = date.fromisoformat(k[0:10]).strftime(\"%m/%d\")\n            if temporal_event in memory:\n                temporal_event += \".\"\n            memory.append(temporal_event)\n            f.write(\n                str(\"\\n'\" + temporal_event + \"'\" + \": \" + str(v) + \",\")\n            )\n        f.write(\"}\")\n\n    dict_clean('temporal', '.')\n    our_ladys_saturday('temporal')\n\n    return None\n\ndef build(diocese='roman', country=None) -> dict:\n    return stitch_calendars(diocese=diocese)\n\n# def liturgical_calendar(year: int):\n#     return importlib.import_module(f'calen.calendar_{year}').LiturgicalCalendar\n\n",      uri = "file:///Users/**********/github/ordo/ordo_tools/utils.py",      version = 0    }  }}
[DEBUG][2023-06-25 16:53:24] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Using interpreter executable: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff",    type = 4  }}
[DEBUG][2023-06-25 16:53:24] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "Running Ruff with: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--format', 'json', '-', '--stdin-filename', '/Users/**********/github/ordo/ordo_tools/utils.py']",    type = 4  }}
[DEBUG][2023-06-25 16:53:24] .../vim/lsp/rpc.lua:361    "rpc.receive"   {  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = { {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 90        },        message = "Line too long (132 > 88 characters)",        range = {          ["end"] = {            character = 132,            line = 89          },          start = {            character = 88,            line = 89          }        },        severity = 2,        source = "Ruff"      }, {        code = "E501",        codeDescription = {          href = "https://beta.ruff.rs/docs/rules/line-too-long"        },        data = {          noqa_row = 311        },        message = "Line too long (100 > 88 characters)",        range = {          ["end"] = {            character = 100,            line = 310          },          start = {            character = 88,            line = 310          }        },        severity = 2,        source = "Ruff"      } },    uri = "file:///Users/**********/github/ordo/ordo_tools/utils.py"  }}
[INFO][2023-06-25 16:53:24] ...lsp/handlers.lua:544 "Using interpreter executable: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff"
[INFO][2023-06-25 16:53:24] ...lsp/handlers.lua:544 "Running Ruff with: /Users/**********/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff ['--force-exclude', '--no-cache', '--no-fix', '--quiet', '--format', 'json', '-', '--stdin-filename', '/Users/**********/github/ordo/ordo_tools/utils.py']"
[DEBUG][2023-06-25 16:53:26] .../vim/lsp/rpc.lua:258    "rpc.send"  {  jsonrpc = "2.0",  method = "textDocument/didSave",  params = {    textDocument = {      uri = "file:///Users/**********/github/ordo/ordo_tools/utils.py"    }  }}

So lsp is certainly working, but I cannot see why it is not being fed into the autocomplete.

VonHeikemen commented 1 year ago

But the Ruff lsp doesn't do completion, it's only for linting.

corei8 commented 1 year ago

Yes! Thank you. I have searched through the available LSPs in Mason for Python and found one that offers completion.

Thank you for your time.