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

Add navigation/find usages/completion support for Nix paths #45

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]
### Added
- Navigation (Ctrl-B/Cmd-B) for Nix paths and path-like strings.
- Directories containing a `default.nix` file offer that file as an optional destination.
- Find usages for files and directories in project view referenced by Nix paths.
- Completion of files and subdirectories for Nix paths.

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.nixos.idea.psi.impl;

import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.nixos.idea.reference.ReferenceUtil;

public class NixLiteralReferencingElementImpl extends NixExprSimpleImpl {
public NixLiteralReferencingElementImpl(@NotNull ASTNode node) {
super(node);
}

@Override
public PsiReference @NotNull [] getReferences() {
return ReferenceUtil.getReferences(this);
}

@Override
public PsiReference getReference() {
return ReferenceUtil.getReference(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.nixos.idea.psi.impl;

import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.nixos.idea.reference.ReferenceUtil;

public class NixStringReferencingElementImpl extends NixStringPartImpl {
public NixStringReferencingElementImpl(@NotNull ASTNode node) {
super(node);
}

@Override
public PsiReference @NotNull [] getReferences() {
return ReferenceUtil.getReferences(this);
}

@Override
public PsiReference getReference() {
return ReferenceUtil.getReference(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.nixos.idea.reference;

import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.nixos.idea.psi.NixLiteral;
import org.nixos.idea.psi.NixStringText;

import java.util.Arrays;

import static com.intellij.patterns.PlatformPatterns.psiElement;
import static com.intellij.patterns.StandardPatterns.or;
import static com.intellij.patterns.StandardPatterns.string;

public class NixReferenceContributor extends PsiReferenceContributor {

// Same as the pattern in Nix.flex.
private static final String NIX_PATH_REGEX = "[a-zA-Z0-9._+-]*(\\/[a-zA-Z0-9._+-]+)+\\/?";

@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar psiReferenceRegistrar) {
psiReferenceRegistrar.registerReferenceProvider(
or(
psiElement(NixLiteral.class),
psiElement(NixStringText.class)
.withText(string().matches(NIX_PATH_REGEX))
Comment on lines +27 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I currently don't quite understand how this is working. How does this code associate which files match against which strings or paths? The sting might also contain escape sequences, shouldn't we have to parse them somewhere?

),
new NixReferenceProvider()
);
}

private static class NixReferenceProvider extends PsiReferenceProvider {

@Override
public boolean acceptsTarget(@NotNull PsiElement target) {
return target instanceof PsiFileSystemItem;
}

@Override
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement psiElement,
@NotNull ProcessingContext processingContext) {
FileReferenceSet fileReferenceSet = new FileReferenceSet(psiElement);
FileReference[] references = fileReferenceSet.getAllReferences();
FileReference lastReference = fileReferenceSet.getLastReference();
FileReference defaultNixReference = fileReferenceSet.createFileReference(lastReference.getRangeInElement(), lastReference.getIndex() + 1, "default.nix");
PsiReference[] allReferences = Arrays.copyOf(references, references.length + 1);
allReferences[allReferences.length - 1] = defaultNixReference;
return allReferences;
}
}
}
18 changes: 18 additions & 0 deletions src/main/java/org/nixos/idea/reference/ReferenceUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.nixos.idea.reference;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceService;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;

public class ReferenceUtil {
public static PsiReference @NotNull [] getReferences(@NotNull PsiElement element) {
return ReferenceProvidersRegistry.getReferencesFromProviders(element, PsiReferenceService.Hints.NO_HINTS);
}

public static PsiReference getReference(@NotNull PsiElement element) {
return ArrayUtil.getFirstElement(getReferences(element));
}
}
6 changes: 6 additions & 0 deletions src/main/lang/Nix.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ expr_simple ::=
| legacy_let
identifier ::= ID
literal ::= INT | FLOAT | PATH | HPATH | SPATH | URI
{
mixin="org.nixos.idea.psi.impl.NixLiteralReferencingElementImpl"
}
parens ::= LPAREN expr recover_parens RPAREN { pin=1 }
set ::= [ REC ] LCURLY recover_set (bind recover_set)* RCURLY { pin=2 }
list ::= LBRAC recover_list (expr_select recover_list)* RBRAC { pin=1 }
Expand All @@ -186,6 +189,9 @@ ind_string ::= IND_STRING_OPEN string_part* IND_STRING_CLOSE { pin=1 }
;{ extends("string_text|antiquotation")=string_part }
string_part ::= string_text | antiquotation { recoverWhile=string_part_recover }
string_text ::= STR | IND_STR
{
mixin="org.nixos.idea.psi.impl.NixStringReferencingElementImpl"
}
Comment on lines 191 to +194
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide some rationale why you are putting the mixin at string_text, not string? One string might contain multiple string_text elements, couldn't this be a problem? I fear that the current implementation would work for ./ in ''hi ''$./'', but not in ''*hi ./''. (I think we should probably detect neither of these cases, and only match against the whole string.)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi I am in good hands I just need some money I am going back in tomorrow and I'm trying my luck and I'm not even trying

antiquotation ::= DOLLAR LCURLY expr recover_antiquotation RCURLY { pin=1 }
private recover_antiquotation ::= { recoverWhile=curly_recover }
private string_part_recover ::= !(STR | IND_STR | DOLLAR | STRING_CLOSE | IND_STRING_CLOSE)
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
id="org.nixos.idea.settings.NixIDEASettings"
instance="org.nixos.idea.settings.NixIDEASettings" />

<psi.referenceContributor language="Nix" implementation="org.nixos.idea.reference.NixReferenceContributor"/>

</extensions>

</idea-plugin>