Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ C:\Users\joshraphael\rascript-language-server_v0.0.1_win-x64.exe
- Syntax Highlighting - Custom RAScript syntax highlighting using TextMate.
- Function navigation - Jump to a functions defintion.
- Code Completion - Completion results appear for symbols as you type.
- Hover Info - Documentation appears when you hover over a function.
- Hover Info - Documentation appears when you hover over a function or class.

## Projects Using rascript-language-server
- [vscode-rascript](https://github.com/joshraphael/vscode-rascript) - VSCode language client for RAScript.
- [sublime-rascript](https://github.com/joshraphael/sublime-rascript) - SublimeText language client for RAScript.
- [sublime-rascript](https://github.com/joshraphael/sublime-rascript) - SublimeText language client for RAScript.
- [npp-rascript](https://github.com/joshraphael/npp-rascript) - Notepad++ language client for RAScript.
2 changes: 1 addition & 1 deletion src/BufferManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public async Task UpdateBufferAsync(string documentPath, StringBuilder buffer, P
}
}
}
p.loadCodeNotes(codeNotes);
// p.loadCodeNotes(codeNotes);
RAScript rascript = new RAScript(documentPath, buffer, p);
_buffers.AddOrUpdate(documentPath, rascript, (k, v) => rascript);
}
Expand Down
26 changes: 22 additions & 4 deletions src/CompletionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,31 @@ public override Task<CompletionList> Handle(CompletionParams request, Cancellati
List<CompletionItem> items = new List<CompletionItem>();
if (parser != null)
{
foreach (var k in parser.GetKeywords())
HashSet<string> functionSet = [.. parser.completionFunctions];
foreach (string fnName in functionSet)
{
CompletionItemKind kind = parser.GetKeywordCompletionItemKind(k) ?? CompletionItemKind.Text;
items.Add(new CompletionItem()
{
Label = k,
Kind = kind,
Label = fnName,
Kind = CompletionItemKind.Function,
});
}
HashSet<string> variableSet = [.. parser.completionVariables];
foreach (string varName in variableSet)
{
items.Add(new CompletionItem()
{
Label = varName,
Kind = CompletionItemKind.Variable,
});
}
HashSet<string> classSet = [.. parser.completionClasses];
foreach (string className in classSet)
{
items.Add(new CompletionItem()
{
Label = className,
Kind = CompletionItemKind.Class,
});
}
}
Expand Down
37 changes: 25 additions & 12 deletions src/DefinitionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,37 @@ public DefinitionProvider(ILanguageServerFacade router, BufferManager bufferMana
public override Task<LocationOrLocationLinks?> Handle(DefinitionParams request, CancellationToken cancellationToken)
{
var documentPath = request.TextDocument.Uri.ToString();
var line = request.Position.Line;
var character = request.Position.Character;
var buffer = _bufferManager.GetBuffer(documentPath);
var txt = buffer?.GetDocumentText();
if (txt != null && txt.Length > 0)
if (buffer != null && txt != null && txt.Length > 0)
{
var word = buffer?.GetParser().GetWordAtPosition(txt, line, character);
if (word != null && word.Length != 0)
var word = buffer.GetParser().GetWordAtPosition(request.Position);
if (word != null && word.Word.Length != 0)
{
Position? pos = buffer?.GetParser().GetLinkLocation(word);
if (pos != null)
int startOffset = buffer.GetParser().GetOffsetAt(word.Start);
int endOffset = buffer.GetParser().GetOffsetAt(word.End);
string hoverClass = buffer.GetParser().DetectClass(startOffset);
if (txt[endOffset+1] != '(')
{
var location = new LocationOrLocationLinks(new LocationOrLocationLink(new Location
return Task.FromResult<LocationOrLocationLinks?>(null); // not a function (maybe string, or just varaible named the same)
}
int origWordOffset = buffer.GetParser().GetOffsetAt(request.Position);
List<ClassFunction>? list = buffer.GetParser().GetClassFunctionDefinitions(word.Word);
if (list != null)
{
WordScope scope = buffer.GetParser().GetScope(word.Start);
List<ClassFunction> filteredList = list.Where(buffer.GetParser().ClassFilter(scope.Global, scope.UsingThis, hoverClass)).ToList();
// can only link to one location, so anything that has multiple definitions wont work for code jumping
if (filteredList.Count == 1)
{
Uri = request.TextDocument.Uri,
Range = new Range(pos, pos)
}));
return Task.FromResult<LocationOrLocationLinks?>(location);
ClassFunction el = filteredList[0];
LocationOrLocationLinks location = new LocationOrLocationLinks(new LocationOrLocationLink(new Location
{
Uri = request.TextDocument.Uri,
Range = new Range(el.Pos, el.Pos)
}));
return Task.FromResult<LocationOrLocationLinks?>(location);
}
}
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/FunctionDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,12 +1078,4 @@ public FunctionDefinitions()
};
}
}

public class FunctionDefinition
{
public required string Key { get; set; }
public required string URL { get; set; }
public required string[] Args { get; set; }
public required string[] CommentDoc { get; set; }
}
}
153 changes: 139 additions & 14 deletions src/HoverProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,153 @@ class HoverProvider(ILanguageServerFacade router, BufferManager bufferManager) :
public override Task<Hover?> Handle(HoverParams request, CancellationToken cancellationToken)
{
var documentPath = request.TextDocument.Uri.ToString();
var line = request.Position.Line;
var character = request.Position.Character;
var t = request.Position;
var buffer = _bufferManager.GetBuffer(documentPath);
var txt = buffer?.GetDocumentText();
if (txt != null && txt.Length > 0)
if (buffer != null && txt != null && txt.Length > 0)
{
var word = buffer?.GetParser().GetWordAtPosition(txt, line, character);
if (word != null && word.Length != 0)
var word = buffer.GetParser().GetWordAtPosition(request.Position);
if (word != null && word.Word.Length != 0)
{
var hoverText = buffer?.GetParser().GetHoverText(word);
if (hoverText != null && hoverText.Length > 0)
int startingOffset = buffer.GetParser().GetOffsetAt(word.Start);
int endingOffset = buffer.GetParser().GetOffsetAt(word.End);
string hoverClass = buffer.GetParser().DetectClass(startingOffset);
int offset = startingOffset - 1;

// Special case: this keyword should show the class hover info
if (word.Word == "this")
{
var content = new List<MarkedString>();
foreach (var l in hoverText)
List<HoverData>? classDefinitions = buffer.GetParser().GetHoverData(hoverClass);
if (classDefinitions != null)
{
content.Add(new MarkedString(l));
foreach (HoverData hoverData in classDefinitions)
{
if (hoverData.ClassName == "")
{
var content = new List<MarkedString>();
foreach (var l in hoverData.Lines)
{
content.Add(new MarkedString(l));
}
Hover result = new()
{
Contents = new MarkedStringsOrMarkupContent(content)
};
return Task.FromResult<Hover?>(result);
}
}
}
Hover result = new()
}
WordScope scope = buffer.GetParser().GetScope(word.Start);
List<HoverData>? definitions = buffer.GetParser().GetHoverData(word.Word);
if (definitions != null)
{
WordType wordType = buffer.GetParser().GetWordType(word);
if (!wordType.Function && !wordType.Class)
{
Contents = new MarkedStringsOrMarkupContent(content.ToArray())
};
return Task.FromResult<Hover?>(result);
// only provide hover data for classes and functions
return Task.FromResult<Hover?>(null);
}
// if we are hovering over the actual function signature itself, find it and return it
foreach (HoverData definition in definitions)
{
// magic number 9 here is length of word function plus a space in between the function name
if (startingOffset >= definition.Index && startingOffset <= definition.Index + 9 + definition.Key.Length)
{
var content = new List<MarkedString>();
foreach (var l in definition.Lines)
{
content.Add(new MarkedString(l));
}
Hover result = new()
{
Contents = new MarkedStringsOrMarkupContent(content)
};
return Task.FromResult<Hover?>(result);
}
}

// determine list of definitions for function calls found in code bodies
List<HoverData> filteredDefinitions = new List<HoverData>();
foreach (HoverData definition in definitions)
{
if (scope.Global)
{
if (definition.ClassName == "")
{
// this should only be one occurence, but we can handle multiple
filteredDefinitions.Add(definition);
}
}
else
{
if (definition.ClassName != "")
{
// Special case: we can determine the exact definition is the definition if using this.<className>
if (scope.UsingThis && hoverClass == definition.ClassName)
{
var content = new List<MarkedString>();
foreach (var l in definition.Lines)
{
content.Add(new MarkedString(l));
}
Hover result = new()
{
Contents = new MarkedStringsOrMarkupContent(content)
};
return Task.FromResult<Hover?>(result);
}
// if its a function, further filter down by arg list length
// otherwise just append if its a class
if (wordType.Function)
{
int numArgs = buffer.GetParser().CountArgsAt(endingOffset);
if (numArgs == definition.Args.Length)
{
filteredDefinitions.Add(definition);
}
}
else
{
filteredDefinitions.Add(definition);
}
}
}
}
if (filteredDefinitions.Count == 1)
{
HoverData definition = filteredDefinitions[0];
var content = new List<MarkedString>();
foreach (var l in definition.Lines)
{
content.Add(new MarkedString(l));
}
Hover result = new()
{
Contents = new MarkedStringsOrMarkupContent(content)
};
return Task.FromResult<Hover?>(result);
}
else
{
// Special case: more than one functions in different classes are named the same and we cant determine the exact hover data
string[] lines = [];
foreach (HoverData defintion in filteredDefinitions)
{
lines = lines.Concat(defintion.Lines).ToArray();
}
HoverData definition = filteredDefinitions[0];
var content = new List<MarkedString>();
foreach (var l in lines)
{
content.Add(new MarkedString(l));
}
Hover result = new()
{
Contents = new MarkedStringsOrMarkupContent(content)
};
return Task.FromResult<Hover?>(result);
}
}
}
}
Expand Down
62 changes: 62 additions & 0 deletions src/Models.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace RAScriptLanguageServer
{
public class ClassScope
{
public required int Start { get; set; }
public required int End { get; set; }
public required Dictionary<string, FunctionDefinition> Functions { get; set; }
public required string[] ConstructorArgs { get; set; }
}
public class CommentBounds
{
public required int Start { get; set; }
public required int End { get; set; }
public required string Type { get; set; }
public required string Raw { get; set; }
}
public class FunctionDefinition
{
public required string Key { get; set; }
public required string URL { get; set; }
public required string[] Args { get; set; }
public required string[] CommentDoc { get; set; }
}

public class ClassFunction
{
public required string ClassName { get; set; }
public required string Name { get; set; }
public required Position Pos { get; set; }
public required string[] Args { get; set; }
}

public class HoverData
{
public required string Key { get; set; }
public required int Index { get; set; }
public required string ClassName { get; set; }
public required string[] Args { get; set; }
public required string[] Lines { get; set; }
}

public class WordLocation
{
public required string Word { get; set; }
public required Position Start { get; set; }
public required Position End { get; set; }
}

public class WordScope
{
public required bool Global { get; set; }
public required bool UsingThis { get; set; }
}

public class WordType
{
public required bool Function { get; set; }
public required bool Class { get; set; }
}
}
Loading