Ale: Configure import paths or a working directory appropriately for each linter

Created on 9 Feb 2017  路  17Comments  路  Source: dense-analysis/ale

In order to successfully lint files, the linter programs need to be informed about the location of other files which are referenced from the original source file. How this can be done depends on the tool in question.

  1. Some programs require running the command from a particular directory, which could be the same directory, or some ancestor directory.
  2. Some programs require arguments which specify where the files referenced are.
  3. Some programs rely on environment variables being set, or configuration files being specified to indicate where the files live.

There is no one catch-all solution which will work for all linters, and _probably_ no one solution which will even work for _most_ linters. Each linter program must be handled separately. Therefore, I recommend listing every linter here which has problems with not properly handling import paths for other files, and spawning separate issues from this issue to handle each distinct case.

What can be implemented generically to aid this process are functions for discovering configuration files (some exist already), functions for formatting escaped file names into commands, or any other utility functions which will be useful in some linters. I recommend developing these as linter solutions are implemented, rather than speculating about theoretical, but potentially impractical, solutions.

Linters to Fix

  • [ ] C/C++ linters, need the location of headers (#277)
  • [ ] pylint, needs to know where Python modules are (#208)
  • [ ] mypy, using a configuration file, maybe extra (#363)
enhancement

Most helpful comment

I had a quick look, and I'm not entirely certain what that plugin does. I generally don't want to depend on another other plugins to get most linters to work.

All 17 comments

The solution for 90% of linters is to create temps in the same directory as the file being edited. otherwise, if it can't be piped to, on save is the only completely reliable method of checking

It's difficult to quantify what percentage of linters a solution like that would work for. For C and C++, knowing the filename of the one source file is not enough information. You also need to know the location of header files, which might not be relative paths. For LESS and SCSS, you need the location of import paths, probably some ancestor directory in most cases.

Some consideration is needed for each linter. For some linters, they simply check syntax in a single file, and don't import anything, so those linters don't suffer from these kinds of issues. For other linters like the D linter, the problem has already largely been solved, especially if you structure your project with DUB configuration files.

substitute(s:aleoutputstring, escape(s:aletemp,".\\"), expand("%"), "")

^for replacing a filename

It's difficult to quantify what percentage of linters a solution like that would work for.

agreed

How would you feel about a configuration option to override the directory in which each linter is executed (or --dir flag or whatever the linter in question uses)? If you could set the path relative to the CWD or file's location then at least users would have a means of fixing import errors. Something like

let g:ale_lint_dirs = {
    'pylint': getcwd()
}

might work. I feel this would be useful for languages like Python with unpredictable project hierarchies.

It would break linters which set their own directories. Really, any linter which needs to executed in a particular directory just needs to be fixed to change directory.

is this about where, or the cmd line args passed specifying where, files are that linters run on?

If a linter doesn't need a makefile to get the filename, the solution for project concerned linters would be to copy the file in question to a temp file in the same directory and run it with that file as the argument, no ?

Copying a single file doesn't inform programs which directories to work in, or which paths to include. The solution is different for each linter, and the problem is that some linters need to be run from particular directories, and/or need to know the paths to files to include.

What about a per-directory/per-project configuration file?

I'm considering this particularly from the C/C++ viewpoint. A plugin like clang_complete gives the user the option of defining the desired compiler options in a configuration file, placed either in the same directory as the source files or in some parent directory. For large C++ projects this can be quite useful, as there is often more to configure than just include directories (global definitions, manually included files, disabled warnings etc.)

I also feel like implementing a feature like this would be easier than some kind of automagic behaviour. One doesn't prevent the other from happening either: perhaps implement per-project configuration first, and work on the algorithms for automatically getting the configuration 'right' later?

Generally, ALE should try and just work without requiring extra configuration files specific to ALE, but I'm not opposed to the idea where it makes sense. In the case of Python projects, files like setup.cfg should be read, or mypy.ini for mypy. Maven files could be read for Java, etc.

Generally we should try and parse any existing configuration files first, before requiring extra effort on the part of users.

what about integrating with https://github.com/tpope/vim-projectionist ? there is already dispatch integration, which is basically :make

I had a quick look, and I'm not entirely certain what that plugin does. I generally don't want to depend on another other plugins to get most linters to work.

I think I'm running into this issue with perl, specifically where the perl uses the FindBin module. This dynamically identifies the location of the script and uses that accordingly. (e.g. use lib "$Bin/lib"). It looks like ALE runs the linter against a temp copy of the file (/tmp/nvimoioTDN/200) which results in the BinDir being that temporary directory rather than the true location of the file being linted. Putting the temp file in the same directory as the actual file would likely solve this and feels like a more correct way of handling things in general.

Creating temporary files inside of project directories creates a separate issue of leaving junk files lying around.

Looks like a workaround would be to run the perl linter with the temporary file as STDIN rather than directly on the temporary file, whilst also doing that with the current working directory set to the location of the real file. I've tested the first bit (simply by setting the options to '<') but not sure how to do working directory bit.

@esran Changing the working directory is actually pretty easy. You just have to prefix the command line string with 'cd ' . fnameescape(fnamemodify(bufname(a:buffer), ':p:h')) . ' && ' . l:rest_of_command. I might add a utility function for that now, as that'll become a pretty common pattern.

You can now write ale#util#BufferCdString(a:buffer) . l:rest_of_command for the same.

I think the separate issues track this better now, so I'm closing this. The utility functions have proven their use for fixing these issues.

Was this page helpful?
0 / 5 - 0 ratings