Skip to content

Commit

Permalink
Support direct use completions, absolute shape ids
Browse files Browse the repository at this point in the history
This commit makes it so you can manually type out a use statement,
and get completions for the absolute shape id. It also adds support
for completion/definition/hover for absolute shape ids in general.
  • Loading branch information
milesziemer committed Jun 12, 2024
1 parent d9f82f9 commit cfe22db
Show file tree
Hide file tree
Showing 9 changed files with 436 additions and 84 deletions.
75 changes: 72 additions & 3 deletions src/main/java/software/amazon/smithy/lsp/document/Document.java
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,19 @@ public CharBuffer borrowToken(Position position) {
* within, or {@code null} if the position is not within an id
*/
public CharBuffer borrowId(Position position) {
DocumentId id = getDocumentIdAt(position);
if (id == null) {
return null;
}
return id.borrowIdValue();
}

/**
* @param position The position within the id to get
* @return A new id that the given {@code position} is
* within, or {@code null} if the position is not within an id
*/
public DocumentId getDocumentIdAt(Position position) {
int idx = indexOfPosition(position);
if (idx < 0) {
return null;
Expand All @@ -329,9 +342,26 @@ public CharBuffer borrowId(Position position) {
return null;
}

boolean hasHash = false;
boolean hasDollar = false;
boolean hasDot = false;
int startIdx = idx;
while (startIdx >= 0) {
if (isIdChar(buffer.charAt(startIdx))) {
char c = buffer.charAt(startIdx);
if (isIdChar(c)) {
switch (c) {
case '#':
hasHash = true;
break;
case '$':
hasDollar = true;
break;
case '.':
hasDot = true;
break;
default:
break;
}
startIdx -= 1;
} else {
break;
Expand All @@ -340,14 +370,53 @@ public CharBuffer borrowId(Position position) {

int endIdx = idx;
while (endIdx < buffer.length()) {
if (isIdChar(buffer.charAt(endIdx))) {
char c = buffer.charAt(endIdx);
if (isIdChar(c)) {
switch (c) {
case '#':
hasHash = true;
break;
case '$':
hasDollar = true;
break;
case '.':
hasDot = true;
break;
default:
break;
}

endIdx += 1;
} else {
break;
}
}

return CharBuffer.wrap(buffer, startIdx + 1, endIdx);
// TODO: This can be improved to do some extra validation, like if
// there's more than 1 hash or $, its invalid. Additionally, we
// should only give a type of *WITH_MEMBER if the position is on
// the member itself. We will probably need to add some logic or
// keep track of the member itself in order to properly match the
// RELATIVE_WITH_MEMBER type in handlers.
DocumentId.Type type;
if (hasHash && hasDollar) {
type = DocumentId.Type.ABSOLUTE_WITH_MEMBER;
} else if (hasHash) {
type = DocumentId.Type.ABSOLUTE_ID;
} else if (hasDollar) {
type = DocumentId.Type.RELATIVE_WITH_MEMBER;
} else if (hasDot) {
type = DocumentId.Type.NAMESPACE;
} else {
type = DocumentId.Type.ID;
}

int actualStartIdx = startIdx + 1; // because we go past the actual start in the loop
CharBuffer wrapped = CharBuffer.wrap(buffer, actualStartIdx, endIdx); // endIdx here is non-inclusive
Position start = positionAtIndex(actualStartIdx);
Position end = positionAtIndex(endIdx - 1); // because we go pas the actual end in the loop
Range range = new Range(start, end);
return new DocumentId(type, wrapped, range);
}

private static boolean isIdChar(char c) {
Expand Down
72 changes: 72 additions & 0 deletions src/main/java/software/amazon/smithy/lsp/document/DocumentId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.lsp.document;

import java.nio.CharBuffer;
import org.eclipse.lsp4j.Range;

/**
* An inaccurate representation of an identifier within a model. It is
* inaccurate in the sense that the string value it references isn't
* necessarily a valid identifier, it just looks like an identifier.
*/
public final class DocumentId {
/**
* Represents the different kinds of identifiers that can be used to match.
*/
public enum Type {
/**
* Just a shape name, no namespace or member.
*/
ID,

/**
* Same as {@link Type#ID}, but with a namespace.
*/
ABSOLUTE_ID,

/**
* Just a namespace - will have one or more {@code .}.
*/
NAMESPACE,

/**
* Same as {@link Type#ABSOLUTE_ID}, but with a member - will have a {@code $}.
*/
ABSOLUTE_WITH_MEMBER,

/**
* Same as {@link Type#ID}, but with a member - will have a {@code $}.
*/
RELATIVE_WITH_MEMBER;
}

private final Type type;
private final CharBuffer buffer;
private final Range range;

DocumentId(Type type, CharBuffer buffer, Range range) {
this.type = type;
this.buffer = buffer;
this.range = range;
}

public Type getType() {
return type;
}

public String copyIdValue() {
return buffer.toString();
}

public CharBuffer borrowIdValue() {
return buffer;
}

public Range getRange() {
return range;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ public DocumentPositionContext determineContext(Position position) {
return DocumentPositionContext.SHAPE_DEF;
} else if (isMixin(position)) {
return DocumentPositionContext.MIXIN;
} else if (isUseTarget(position)) {
return DocumentPositionContext.USE_TARGET;
} else {
return DocumentPositionContext.OTHER;
}
Expand Down Expand Up @@ -382,7 +384,7 @@ private boolean isMixin(Position position) {
}

jumpToPosition(document.positionAtIndex(lastWithIndex));
if (!isSp(-1)) {
if (!isWs(-1)) {
return false;
}
skip();
Expand Down Expand Up @@ -483,6 +485,35 @@ private boolean isMemberTarget(Position position) {
return position() >= idx;
}

private boolean isUseTarget(Position position) {
int idx = document.indexOfPosition(position);
if (idx < 0) {
return false;
}
int lineStartIdx = document.indexOfLine(document.lineOfIndex(idx));

int useIdx = nextIndexOfWithOnlyLeadingWs("use", lineStartIdx, idx);
if (useIdx < 0) {
return false;
}

jumpToPosition(document.positionAtIndex(useIdx));

skip(); // u
skip(); // s
skip(); // e

if (!isSp()) {
return false;
}

sp();

skipShapeId();

return position() >= idx;
}

private boolean jumpToPosition(Position position) {
int idx = this.document.indexOfPosition(position);
if (idx < 0) {
Expand Down Expand Up @@ -572,8 +603,17 @@ private boolean isSp() {
return is(' ') || is('\t');
}

private boolean isSp(int offset) {
return is(' ', offset) || is('\t', offset);
private boolean isWs(int offset) {
char peeked = peek(offset);
switch (peeked) {
case '\n':
case '\r':
case ' ':
case '\t':
return true;
default:
return false;
}
}

private boolean isEof() {
Expand Down Expand Up @@ -634,7 +674,11 @@ private boolean isShapeType() {
}

private int firstIndexOfWithOnlyLeadingWs(String s) {
int searchFrom = 0;
return nextIndexOfWithOnlyLeadingWs(s, 0, document.length());
}

private int nextIndexOfWithOnlyLeadingWs(String s, int start, int end) {
int searchFrom = start;
int previousSearchFrom;
do {
int idx = document.nextIndexOf(s, searchFrom);
Expand All @@ -654,7 +698,7 @@ private int firstIndexOfWithOnlyLeadingWs(String s) {
}
previousSearchFrom = searchFrom;
searchFrom = idx + 1;
} while (previousSearchFrom != searchFrom && searchFrom < document.length());
} while (previousSearchFrom != searchFrom && searchFrom < end);
return -1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public enum DocumentPositionContext {
*/
MIXIN,

/**
* Within the target (shape id) of a {@code use} statement.
*/
USE_TARGET,

/**
* An unknown or indeterminate position.
*/
Expand Down
Loading

0 comments on commit cfe22db

Please sign in to comment.