Skip to content

IntelliSense should work across dot-sourced file references #144

Open

Description

@jdkang

Is it possible to support dot sourcing to pull Intellisense across files?

e.g.

\
\a.ps1
\b.ps1
# a.ps1
function Get-Foo($baz) { write-host $baz }

# b.ps1
. $PsScriptRoot\b.ps1

Get-Foo -baz

Activity

  1. daviwil commented on Apr 15, 2016

    @daviwil
    Contributor

    Right now the only way to make that work is for you to select the contents of b.ps1 and run them with F8. This will cause the dot-source reference to be evaluated so that the Get-Foo method is in your session.

    I've been thinking about how I could make this work by default without having to evaluate the file but it might require changes to PowerShell's underlying completion engine. Will definitely look into this in the future but probably won't get to it for a little while. Thanks!

  2. changed the title [-]Ability to support dot sourcing[/-] [+]IntelliSense should work across dot-sourced file references[/+] on Apr 15, 2016
  3. added this to the milestone on Apr 15, 2016
  4. Leon99 commented on Oct 26, 2016

    @Leon99

    This is an extremely important feature. It will significantly improve PowerShell development experience.
    I wonder if there is a way to "trick" the completion engine to treat the modules in the opened folder the same way it treats the standard library?

  5. daviwil commented on Oct 26, 2016

    @daviwil
    Contributor

    Leon V (@Leon99) Not sure, I'll have to ask around and see. Good idea though!

  6. rkeithhill commented on Oct 27, 2016

    @rkeithhill
    Contributor

    David Wilson (@daviwil) Wouldn't this work in the new system where we have a PowerShell interactive console as long as you F8 the lines that dot source other scripts? That works in ISE.

  7. Leon99 commented on Oct 27, 2016

    @Leon99

    If it's possible to modify $profile file and then re-init auto-completion engine, it'd be an easy job. Hook onto opening a folder, get the list of .psm1 files available, import them through $profile, init auto-complete. Done.

  8. Glober777 commented on Oct 27, 2016

    @Glober777

    Approach suggested by Leon V (@Leon99) sounds interesting, although the import would have to also automatically reoccur every time those module files change, as IntelliSense may continue suggesting things based on the previous version of the module.

  9. daviwil commented on Oct 27, 2016

    @daviwil
    Contributor

    Keith Hill (@rkeithhill) Yep, that'd work, though I think the expectation is that the user wouldn't need to do that.

    Leon V (@Leon99) Glober777 That's one option I suppose, but I'd rather find a way that either allows the existing completion engine to become aware of file relationships or find another way using my own analysis so that no code needs to be evaluated before the IntelliSense results come back.

    I've got plans for creating a module which can analyze a folder of script files and create an updatable index of everything in those files, taking into account relationships between files (like those dot-sourced into a .psm1, etc). Once I have this index, I might be able to use it in addition to the completion results that come back from PowerShell's completion engine.

    However, I think it might be better to first see if this is something desirable to add to the built-in completion engine itself before running off to build my own solution :)

  10. Leon99 commented on Oct 28, 2016

    @Leon99

    David Wilson (@daviwil) Sounds like a proper plan. My only concern is that it may take a lot of effort to implement, that's why I suggested a "low budget" idea. Well, at least I think it is. In fact, for me it'd be okay even if there was a command that I need run manually to import all the modules in the current folder plus do some magic to make sure that IntelliSense sees it.

  11. mvvsubbu commented on Sep 29, 2017

    @mvvsubbu

    Is this something in your radar to be implemented anytime soon?

  12. Arunideas commented on Apr 24, 2018

    @Arunideas

    This is really a important feature to make sure VScode as ISE is less preferred for Powershell developers after seeing the Vscode :) Am ne of those eagerly waiting 😀

  13. removed this from the milestone on May 24, 2018
  14. 7 remaining items

  15. PLogFinder commented on Apr 14, 2020

    @PLogFinder

    For a user like me, this is what I can envisage, borrowing from old visual basic for applications (VBA) in office - we had a references menu item and dialogue, and could select modules we wanted to use, and the intellisense for those modules then worked while we edited our VBA macros.

    To adapt to VSCode/Powershell extension context: would you consider having a References menu item to collect a list of module/script files that either the user selects, or can be auto-populated using your automated method? So, a VSCode menu item 'References' that displays the list, with a command to run to auto-populate (maybe that command also runs if the user clicks the References menu item when it's empty), and a dialogue where the user can refine the list if the list already exists (and after running the auto-populate command)? The list gets saved in the folder's project settings (not up with the jargon for that), so the references are pre-set next time the user opens that folder. To decide if the References list applies per-file (where the listed references and intellisense are active for the currently open file) or per-folder (where the listed references and intellisense are active for any open file from that folder), or maybe can have two parts, the file-level references and the folder-level references (dialogue for References list would accordingly be in two parts).

    This gives the user control of the extent/scope of references, rather than forcing them to accept everything that's thrown in by an automated analyser (I do think that starting off with the analyser is super convenient, though)

    About PSModulePath - I differentiate between my development environment and the intended execution environment where I don't control PSModulePath, so I'd want to keep my PSModulePath clean (as powershell fresh install) and use another way (the References list I suggest above) to make use of modules in my development editor. When test-running, then, the script is running in a clean environmment similar to production.

    Hope this makes sense.

  16. Glober777 commented on Apr 14, 2020

    @Glober777

    I like the approach suggested by PLogFinder (@PLogFinder) with one minor addition. I'd like to avoid updating this list very often for each project, so it would be really great if it would support wildcards, where I'd specify something like:

    {
       "ReferencesList": [
        "${WorkspaceRoot}\\ModuleName\\*.ps1"
       ]
    }

    or:

    {
       "ReferencesList": [
        "${WorkspaceRoot}\\ModuleName\\Public\\*.ps1",
        "${WorkspaceRoot}\\ModuleName\\Private\\*.ps1",
       ]
    }

    That would probably not catch things that may be declared directly within the ModuleName.psm1 file, but still would surely be better.

  17. SeeminglyScience commented on Apr 14, 2020

    @SeeminglyScience
    Collaborator

    Rob Holt (@rjmholt) btw I did experiment with rebuilding the AST of multiple files. It works... sort of. Too much in PowerShell's completion API relies on extent. So either we'd have to fake the extents (which while doable seems like a whole other can of worms) or just have it be very buggy.

    I'm not confident that there's anything this extension can do to accomplish this, short of lifting the entire completion API and rewriting large parts of it.

  18. drlsdee commented on Jul 21, 2020

    @drlsdee

    Yes, I would like to see such an opportunity too. I think this would be especially useful when writing custom classes, inheriting them, and separating the code of the classes from the code of the functions using these classes. Something like this:

    \$PSScriptRoot\             # The module's root folder.
    \Classes\                   # All classes are here. Each script contains only one class; each script is named exactly like
                                # the class it contains. Classes inherited from others should be placed in subfolders, i.e.:
    \Classes\Foo\Foo.ps1        # The "Foo" class has no descendants or ascendants or any other references to other classes.
    \Classes\Bar\Bar.ps1        # The class "Bar" has descendants.
    \Classes\Bar\Com\Com.ps1    # The class "Com" inherited from the class "Bar": "Com : Bar"
    \Functions\                 # All functions are here. Each script contains only one function; each script is named exactly
                                # like the function it contains.
    \Functions\Public\*.ps1     # Public functions.
    \Functions\Private\*.ps1    # Private functions.
    ...
    ...                         # "DSCResources", "Tests", nested modules et cetera.
    ...
    \"$($moduleName).psm1"      # The main module. The code in this file should list all the applicable files in the module's
                                # folder structure and import them during module import: first classes
                                # (in directory nesting order), then functions, etc.
    \"$($moduleName).psd1"      # The module manifest.
    

    As a workaround, now instead of pressing "F5" (or "F8") I have to write some additional function that will import the module with all its classes and call some function from the module. Is this called TDD? J

  19. removed this from the milestone on Jan 26, 2021
  20. XabiBeltza commented on Feb 13, 2021

    @XabiBeltza

    So, little chance of this getting developed?

  21. SydneyhSmith commented on Feb 16, 2021

    @SydneyhSmith
    Collaborator

    XabiBeltza working on this issue is not currently in our short-medium term plans

  22. rjmholt commented on Feb 17, 2021

    @rjmholt
    Contributor

    Honestly a feature like this is only possible to make work in scenarios where the dot-sourced path is known statically anyway.

    Consider this:

    $path = "$PSScriptRoot/script/is/here.ps1"
    
    . $path

    In this case, we must do some form of constant-folding to determine the outcome of the dot-sourcing. So it's possible here, but requires non-trivial effort (i.e. special AST processing) to make work. At the point where we're manipulating an AST, we now have to juggle how that works with PowerShell, since PowerShell itself is providing the intellisense (we just call into its API). So we get to the point where what the user sees isn't what PowerShell sees, adding considerable complexity.

    But then there are worse cases:

    $path = "./script/is/here.ps1"
    
    . $path

    In this case the outcome depends on the session's current location.

    Or indeed:

    $path = Get-Content -Raw ./paths.json | ConvertFrom-Json | Select -First 1
    
    . $path

    This is a rather pathological case (although I can come up with significantly worse), but the point is that there's no general approach to making intellisense work across dot-sourcing that doesn't run the script. Dot-sourcing is a runtime concept.

    Worse than this though, there's no guarantee that the filesystem layout at edit-time will be the same as the layout after deployment (at runtime). If you have a module where you lay all your pieces out in individual files and then have a main thing that dot-sources them all in, but then you lay all the files out differently when you "build" the module, then this feature isn't going to help you.

    Finally, I'll also say that we are an open-source project. We don't have any near-term plans to attempt an implementation here internally, but we're always open to contributions and have accepted them in the past.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions