diff --git a/server/cmd/stdlib_indexer/blurp.go b/server/cmd/stdlib_indexer/blurp.go
index 23cae0c7..0e1061c8 100644
--- a/server/cmd/stdlib_indexer/blurp.go
+++ b/server/cmd/stdlib_indexer/blurp.go
@@ -129,6 +129,7 @@ func Generate_enum(enum *s.Enum, module *s.Module) jen.Code {
jen.Lit(module.GetDocumentURI()),
).
Dot("WithAssociativeValues").Call(associativeValues).
+ Dot("WithEnumName").Call(jen.Lit(enum.GetName())).
Dot("Build").Call(),
)
}
@@ -155,8 +156,10 @@ func Generate_fault(fault *s.Fault, module *s.Module) jen.Code {
jen.Qual(PackageName+"symbols", "NewFaultConstantBuilder").
Call(
jen.Lit(enumerator.GetName()),
+ jen.Lit(module.GetName()),
jen.Lit(enumerator.GetDocumentURI()),
).
+ Dot("WithFaultName").Call(jen.Lit(fault.GetName())).
Dot("Build").Call(),
)
}
diff --git a/server/internal/lsp/search/find_parent.go b/server/internal/lsp/search/find_parent.go
index b90565f4..384b7f07 100644
--- a/server/internal/lsp/search/find_parent.go
+++ b/server/internal/lsp/search/find_parent.go
@@ -4,9 +4,50 @@ import (
"github.com/pherrymason/c3-lsp/internal/lsp/project_state"
"github.com/pherrymason/c3-lsp/internal/lsp/search_params"
"github.com/pherrymason/c3-lsp/pkg/document/sourcecode"
+ "github.com/pherrymason/c3-lsp/pkg/option"
"github.com/pherrymason/c3-lsp/pkg/symbols"
)
+// If `true`, this indexable is a type, and so one can access its parent type's (itself)
+// associated members, as well as methods.
+// If `false`, this indexable is a variable or similar, so its parent type is distinct
+// from the indexable itself, therefore only methods can be accessed.
+func canReadMembersOf(s symbols.Indexable) bool {
+ switch s.(type) {
+ case *symbols.Struct, *symbols.Enum, *symbols.Fault:
+ return true
+ case *symbols.Def:
+ // If Def resolves to a type, it can receive its members.
+ return s.(*symbols.Def).ResolvesToType()
+ default:
+ return false
+ }
+}
+
+// Search for a method for 'parentTypeName' given a symbol to search.
+//
+// Returns updated search parameters to progress the search, as well as
+// the search result.
+func (s *Search) findMethod(
+ parentTypeName string,
+ searchingSymbol sourcecode.Word,
+ docId option.Option[string],
+ searchParams search_params.SearchParams,
+ projState *project_state.ProjectState,
+ debugger FindDebugger,
+) (search_params.SearchParams, SearchResult) {
+ // Search in methods
+ methodSymbol := sourcecode.NewWord(parentTypeName+"."+searchingSymbol.Text(), searchingSymbol.TextRange())
+ iterSearch := search_params.NewSearchParamsBuilder().
+ WithSymbolWord(methodSymbol).
+ WithDocId(docId.Get()).
+ WithContextModuleName(searchParams.ModuleInCursor()).
+ WithScopeMode(search_params.InModuleRoot).
+ Build()
+
+ return iterSearch, s.findClosestSymbolDeclaration(iterSearch, projState, debugger.goIn())
+}
+
func (s *Search) findInParentSymbols(searchParams search_params.SearchParams, projState *project_state.ProjectState, debugger FindDebugger) SearchResult {
accessPath := searchParams.GetFullAccessPath()
state := NewFindParentState(accessPath)
@@ -29,6 +70,7 @@ func (s *Search) findInParentSymbols(searchParams search_params.SearchParams, pr
elm := result.Get()
protection := 0
+ membersReadable := true
for {
if protection > 500 {
@@ -36,6 +78,12 @@ func (s *Search) findInParentSymbols(searchParams search_params.SearchParams, pr
}
protection++
+ // Check for readable members before converting the element from a variable
+ // to its parent type, so we can know whether we were originally searching
+ // a variable, from which we cannot read members (enum values and fault
+ // constants).
+ membersReadable = canReadMembersOf(elm)
+
for {
if !isInspectable(elm) {
elm = s.resolve(elm, docId.Get(), searchParams.ModuleInCursor(), projState, symbolsHierarchy, debugger)
@@ -58,65 +106,168 @@ func (s *Search) findInParentSymbols(searchParams search_params.SearchParams, pr
enumerator := elm.(*symbols.Enumerator)
assocValues := enumerator.AssociatedValues
searchingSymbol := state.GetNextSymbol()
+ foundAssoc := false
for i := 0; i < len(assocValues); i++ {
if assocValues[i].GetName() == searchingSymbol.Text() {
elm = &assocValues[i]
symbolsHierarchy = append(symbolsHierarchy, elm)
state.Advance()
+ foundAssoc = true
break
}
}
+ if !foundAssoc && enumerator.GetModuleString() != "" && enumerator.GetEnumName() != "" {
+ // Search in methods
+ // First get the enum
+ enumSymbols := projState.SearchByFQN(enumerator.GetEnumFQN())
+ if len(enumSymbols) > 0 {
+ // Search the enum's methods
+ newIterSearch, result := s.findMethod(
+ enumSymbols[0].GetName(),
+ searchingSymbol,
+ docId,
+ searchParams,
+ projState,
+ debugger,
+ )
+ if result.IsNone() {
+ return NewSearchResultEmpty(trackedModules)
+ }
+ iterSearch = newIterSearch
+ elm = result.Get()
+ symbolsHierarchy = append(symbolsHierarchy, elm)
+ state.Advance()
+ }
+ }
+
+ case *symbols.FaultConstant:
+ constant := elm.(*symbols.FaultConstant)
+
+ if constant.GetModuleString() != "" && constant.GetFaultName() != "" {
+ // Search in methods
+ // First get the fault
+ faultSymbols := projState.SearchByFQN(constant.GetFaultFQN())
+ if len(faultSymbols) > 0 {
+ // Search the fault's methods
+ searchingSymbol := state.GetNextSymbol()
+ newIterSearch, result := s.findMethod(
+ faultSymbols[0].GetName(),
+ searchingSymbol,
+ docId,
+ searchParams,
+ projState,
+ debugger,
+ )
+ if result.IsNone() {
+ return NewSearchResultEmpty(trackedModules)
+ }
+ iterSearch = newIterSearch
+ elm = result.Get()
+ symbolsHierarchy = append(symbolsHierarchy, elm)
+ state.Advance()
+ }
+ }
+
case *symbols.Enum:
_enum := elm.(*symbols.Enum)
- enumerators := _enum.GetEnumerators()
+ foundMemberOrAssoc := false
searchingSymbol := state.GetNextSymbol()
- foundMember := false
- for i := 0; i < len(enumerators); i++ {
- if enumerators[i].GetName() == searchingSymbol.Text() {
- elm = enumerators[i]
- symbolsHierarchy = append(symbolsHierarchy, elm)
- state.Advance()
- foundMember = true
- break
+
+ // 'CoolEnum.VARIANT.VARIANT' is invalid (member not readable on member)
+ // But 'CoolEnum.VARIANT' is ok,
+ // as well as 'AliasForEnum.VARIANT'
+ if membersReadable {
+ enumerators := _enum.GetEnumerators()
+ for i := 0; i < len(enumerators); i++ {
+ if enumerators[i].GetName() == searchingSymbol.Text() {
+ elm = enumerators[i]
+ symbolsHierarchy = append(symbolsHierarchy, elm)
+ state.Advance()
+ foundMemberOrAssoc = true
+ break
+ }
+ }
+ } else {
+ // Members not readable => this is an instance, so we can read associated values.
+ assocs := _enum.GetAssociatedValues()
+ for i := 0; i < len(assocs); i++ {
+ if assocs[i].GetName() == searchingSymbol.Text() {
+ elm = &assocs[i]
+ symbolsHierarchy = append(symbolsHierarchy, elm)
+ state.Advance()
+ foundMemberOrAssoc = true
+ break
+ }
}
}
- if !foundMember {
+
+ if !foundMemberOrAssoc {
// Search in methods
- methodSymbol := sourcecode.NewWord(_enum.GetName()+"."+searchingSymbol.Text(), searchingSymbol.TextRange())
- iterSearch = search_params.NewSearchParamsBuilder().
- WithSymbolWord(methodSymbol).
- WithDocId(docId.Get()).
- WithContextModuleName(searchParams.ModuleInCursor()).
- WithScopeMode(search_params.InModuleRoot).
- Build()
- result := s.findClosestSymbolDeclaration(iterSearch, projState, debugger.goIn())
+ newIterSearch, result := s.findMethod(
+ _enum.GetName(),
+ searchingSymbol,
+ docId,
+ searchParams,
+ projState,
+ debugger,
+ )
if result.IsNone() {
return NewSearchResultEmpty(trackedModules)
}
-
+ iterSearch = newIterSearch
elm = result.Get()
symbolsHierarchy = append(symbolsHierarchy, elm)
state.Advance()
}
case *symbols.Fault:
- _enum := elm.(*symbols.Fault)
- constants := _enum.GetConstants()
+ fault := elm.(*symbols.Fault)
searchingSymbol := state.GetNextSymbol()
- for i := 0; i < len(constants); i++ {
- if constants[i].GetName() == searchingSymbol.Text() {
- elm = constants[i]
- symbolsHierarchy = append(symbolsHierarchy, elm)
- state.Advance()
- break
+ foundMember := false
+
+ if membersReadable {
+ constants := fault.GetConstants()
+ for i := 0; i < len(constants); i++ {
+ if constants[i].GetName() == searchingSymbol.Text() {
+ elm = constants[i]
+ symbolsHierarchy = append(symbolsHierarchy, elm)
+ state.Advance()
+ foundMember = true
+ break
+ }
}
}
+
+ if !foundMember {
+ // Search in methods
+ newIterSearch, result := s.findMethod(
+ fault.GetName(),
+ searchingSymbol,
+ docId,
+ searchParams,
+ projState,
+ debugger,
+ )
+ if result.IsNone() {
+ return NewSearchResultEmpty(trackedModules)
+ }
+ iterSearch = newIterSearch
+ elm = result.Get()
+ symbolsHierarchy = append(symbolsHierarchy, elm)
+ state.Advance()
+ }
+
case *symbols.Struct:
strukt, _ := elm.(*symbols.Struct)
members := strukt.GetMembers()
searchingSymbol := state.GetNextSymbol()
foundMember := false
+
+ // Members are always readable when the parent type is struct
+ // TODO: Maybe we should actually check for NOT membersReadable,
+ // if anonymous substructs are found to not be usable anywhere
+ // (Can't write methods for them, for example)
for i := 0; i < len(members); i++ {
if members[i].GetName() == searchingSymbol.Text() {
elm = members[i]
@@ -129,18 +280,18 @@ func (s *Search) findInParentSymbols(searchParams search_params.SearchParams, pr
if !foundMember {
// Search in methods
- methodSymbol := sourcecode.NewWord(strukt.GetName()+"."+searchingSymbol.Text(), searchingSymbol.TextRange())
- iterSearch = search_params.NewSearchParamsBuilder().
- WithSymbolWord(methodSymbol).
- WithDocId(docId.Get()).
- WithContextModuleName(searchParams.ModuleInCursor()).
- WithScopeMode(search_params.InModuleRoot).
- Build()
- result := s.findClosestSymbolDeclaration(iterSearch, projState, debugger.goIn())
+ newIterSearch, result := s.findMethod(
+ strukt.GetName(),
+ searchingSymbol,
+ docId,
+ searchParams,
+ projState,
+ debugger,
+ )
if result.IsNone() {
return NewSearchResultEmpty(trackedModules)
}
-
+ iterSearch = newIterSearch
elm = result.Get()
symbolsHierarchy = append(symbolsHierarchy, elm)
state.Advance()
@@ -151,6 +302,7 @@ func (s *Search) findInParentSymbols(searchParams search_params.SearchParams, pr
break
}
}
+ searchResult.SetMembersReadable(membersReadable)
searchResult.Set(elm)
return searchResult
diff --git a/server/internal/lsp/search/search_closest_declaration_test.go b/server/internal/lsp/search/search_closest_declaration_test.go
index cda26db2..550b47e0 100644
--- a/server/internal/lsp/search/search_closest_declaration_test.go
+++ b/server/internal/lsp/search/search_closest_declaration_test.go
@@ -34,8 +34,21 @@ func initTestEnv() (*project_state.ProjectState, map[string]document.Document) {
return &language, documents
}*/
-func buildPosition(line uint, character uint) idx.Position {
- return idx.Position{Line: line - 1, Character: character}
+func SearchUnderCursor_ClosestDecl(body string, optionalState ...TestState) option.Option[idx.Indexable] {
+ state := NewTestState()
+ search := NewSearchWithoutLog()
+
+ if len(optionalState) > 0 {
+ state = optionalState[0]
+ }
+
+ cursorlessBody, position := parseBodyWithCursor(body)
+ state.registerDoc(
+ "app.c3",
+ cursorlessBody,
+ )
+
+ return search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
}
var debugger = NewFindDebugger(true)
@@ -99,22 +112,14 @@ func TestLanguage_findClosestSymbolDeclaration_ignores_keywords(t *testing.T) {
}
func TestLanguage_findClosestSymbolDeclaration_variables(t *testing.T) {
- state := NewTestState()
- search := NewSearchWithoutLog()
-
t.Run("Find global variable definition, with cursor in usage", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`int number = 0;
fn void newNumber(){
- int result = number + 10;
+ int result = n|||umber + 10;
}`,
)
- position := buildPosition(3, 18) // Cursor at `n|umber`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
-
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -124,17 +129,12 @@ func TestLanguage_findClosestSymbolDeclaration_variables(t *testing.T) {
})
t.Run("Find local variable definition, with cursor in same declaration", func(t *testing.T) {
- state.registerDoc(
- "number.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fn void newNumber(){
- int number;
+ int n|||umber;
}`,
)
- position := buildPosition(2, 9) // Cursor at `n|umber`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("number.c3", position, &state.state)
-
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -144,16 +144,12 @@ func TestLanguage_findClosestSymbolDeclaration_variables(t *testing.T) {
})
t.Run("Find local variable definition from usage", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fn Emu newEmulator(){
Emu emulator;
- emulator = 2;
+ e|||mulator = 2;
}`,
)
- position := buildPosition(3, 5) // Cursor at `e|mulator`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -164,16 +160,12 @@ func TestLanguage_findClosestSymbolDeclaration_variables(t *testing.T) {
})
t.Run("Should find the right element when there is a different element with the same name up in the scope", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`char ambiguousVariable = 'C';
fn void main() {
- int ambiguousVariable = 3;
+ int a|||mbiguousVariable = 3;
}`,
)
- position := buildPosition(3, 9) // Cursor a|mbiguousVariable
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -184,15 +176,11 @@ func TestLanguage_findClosestSymbolDeclaration_variables(t *testing.T) {
})
t.Run("Find local variable definition in function arguments", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fn void run(int tick) {
- tick = tick + 3;
+ t|||ick = tick + 3;
}`,
)
- position := buildPosition(2, 5) // Cursor at `t|ick = tick + 3;`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.True(t, symbolOption.IsSome(), "Element not found")
@@ -204,24 +192,16 @@ func TestLanguage_findClosestSymbolDeclaration_variables(t *testing.T) {
// Tests related to structs:
func TestLanguage_findClosestSymbolDeclaration_structs(t *testing.T) {
- state := NewTestState()
- search := NewSearchWithoutLog()
-
t.Run("Should find struct declaration in variable declaration", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`struct Emu {
bool a;
}
fn void main() {
- Emu emulator;
+ E|||mu emulator;
}`,
)
- position := buildPosition(5, 5) // Cursor at `E|mu emulator`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
-
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -230,18 +210,14 @@ func TestLanguage_findClosestSymbolDeclaration_structs(t *testing.T) {
})
t.Run("Should find struct declaration in function return type", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`struct Emu {
bool a;
}
- fn Emu main() {
+ fn E|||mu main() {
Emu emulator;
}`,
)
- position := buildPosition(4, 7) // Cursor at `fn E|mu main() {`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -250,19 +226,15 @@ func TestLanguage_findClosestSymbolDeclaration_structs(t *testing.T) {
})
t.Run("Should find interface struct is implementing", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`interface EmulatorConsole
{
fn void run();
}
- struct Emu (EmulatorConsole) {
+ struct Emu (|||EmulatorConsole) {
bool a;
}`,
)
- position := buildPosition(5, 15) // Cursor is at struct Emu (E|mulatorConsole) {
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
_interface, ok := symbolOption.Get().(*idx.Interface)
@@ -274,20 +246,13 @@ func TestLanguage_findClosestSymbolDeclaration_structs(t *testing.T) {
}
func TestLanguage_findClosestSymbolDeclaration_enums(t *testing.T) {
- state := NewTestState()
- search := NewSearchWithoutLog()
-
t.Run("Find local enum variable definition when cursor is in enum declaration", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
fn void main() {
- WindowStatus status;
+ WindowStatus st|||atus;
}`,
)
- position := buildPosition(3, 19)
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
@@ -297,16 +262,12 @@ func TestLanguage_findClosestSymbolDeclaration_enums(t *testing.T) {
})
t.Run("Should find enum definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
fn void main() {
- WindowStatus status;
+ W|||indowStatus status;
}`,
)
- position := buildPosition(3, 5) // Cursor is at `W|indowStatus status;`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
@@ -315,17 +276,13 @@ func TestLanguage_findClosestSymbolDeclaration_enums(t *testing.T) {
})
t.Run("Should find local explicit enumerator definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
fn void main() {
WindowStatus status;
- status = WindowStatus.BACKGROUND;
+ status = WindowStatus.B|||ACKGROUND;
}`,
)
- position := buildPosition(4, 27) // Cursor is at `status = WindowStatus.B|ACKGROUND`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
_, ok := symbolOption.Get().(*idx.Enumerator)
@@ -333,21 +290,60 @@ func TestLanguage_findClosestSymbolDeclaration_enums(t *testing.T) {
assert.Equal(t, "BACKGROUND", symbolOption.Get().GetName())
})
+ t.Run("Should not find enumerator on enumerator", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ fn void main() {
+ WindowStatus status;
+ status = WindowStatus.BACKGROUND.M|||INIMIZED;
+ }`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element found")
+ })
+
+ t.Run("Should not find enumerator on enumerator variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ fn void main() {
+ WindowStatus status = WindowStatus.BACKGROUND;
+ status = status.M|||INIMIZED;
+ }`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element found")
+ })
+
t.Run("Should find local enumerator definition associated value", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`enum WindowStatus : int (int counter) {
OPEN = 1,
BACKGROUND = 2,
MINIMIZED = 3
}
fn void main() {
- int status = WindowStatus.BACKGROUND.counter;
+ int status = WindowStatus.BACKGROUND.c|||ounter;
}`,
)
- position := buildPosition(7, 42) // Cursor is at `status = WindowStatus.BACKGROUND.c|ounter`
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not an associated value, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "counter", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should find local enumerator definition associated value without custom backing type", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus : (int counter) {
+ OPEN = 1,
+ BACKGROUND = 2,
+ MINIMIZED = 3
+ }
+ fn void main() {
+ int status = WindowStatus.BACKGROUND.c|||ounter;
+ }`,
+ )
assert.False(t, symbolOption.IsNone(), "Element not found")
variable, ok := symbolOption.Get().(*idx.Variable)
@@ -356,18 +352,70 @@ func TestLanguage_findClosestSymbolDeclaration_enums(t *testing.T) {
assert.Equal(t, "int", variable.GetType().GetName())
})
+ t.Run("Should find associated value on enum instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus : int (int counter) {
+ OPEN = 1,
+ BACKGROUND = 2,
+ MINIMIZED = 3
+ }
+ fn void main() {
+ WindowStatus status = WindowStatus.BACKGROUND;
+ int value = status.c|||ounter;
+ }`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not an associated value, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "counter", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should find associated value on enum instance struct member", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus : int (int counter) {
+ OPEN = 1,
+ BACKGROUND = 2,
+ MINIMIZED = 3
+ }
+ struct MyStruct { WindowStatus stat; }
+ fn void main() {
+ MyStruct wrapper = { WindowStatus.BACKGROUND };
+ int value = wrapper.stat.c|||ounter;
+ }`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not an associated value, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "counter", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should not find associated value on enum type", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus : int (int counter) {
+ OPEN = 1,
+ BACKGROUND = 2,
+ MINIMIZED = 3
+ }
+ fn void main() {
+ WindowStatus.c|||ounter;
+ }`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element was found")
+ })
+
t.Run("Should find local implicit enumerator definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
fn void main() {
WindowStatus status;
- status = BACKGROUND;
+ status = |||BACKGROUND;
}`,
)
- position := buildPosition(4, 13) // Cursor is at `status = B|ACKGROUND`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
_, ok := symbolOption.Get().(*idx.Enumerator)
@@ -375,45 +423,51 @@ func TestLanguage_findClosestSymbolDeclaration_enums(t *testing.T) {
assert.Equal(t, "BACKGROUND", symbolOption.Get().GetName())
})
- t.Run("Should find enum method definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ t.Run("Should find enum method definition on instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
`enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
fn bool WindowStatus.isOpen(){}
fn void main() {
WindowStatus val = OPEN;
- val.isOpen();
+ val.is|||Open();
}
`,
)
- position := buildPosition(6, 10) // Cursor is at `e.is|Open()`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
_, ok := symbolOption.Get().(*idx.Function)
assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not a method, %s was found", reflect.TypeOf(symbolOption.Get())))
assert.Equal(t, "WindowStatus.isOpen", symbolOption.Get().GetName())
})
+
+ t.Run("Should find enum method definition on explicit enumerator", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ fn bool WindowStatus.isOpen(){}
+
+ fn void main() {
+ WindowStatus.OPEN.isO|||pen();
+ }
+ `,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ _, ok := symbolOption.Get().(*idx.Function)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a method, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "WindowStatus.isOpen", symbolOption.Get().GetName())
+ })
}
func TestLanguage_findClosestSymbolDeclaration_faults(t *testing.T) {
- state := NewTestState()
- search := NewSearchWithoutLog()
-
t.Run("Find local fault definition in type declaration", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
fn void main() {
- WindowError error = WindowError.SOMETHING_HAPPENED;
+ W|||indowError error = WindowError.SOMETHING_HAPPENED;
error = UNEXPECTED_ERROR;
}`,
)
- position := buildPosition(3, 5) // Cursor at `W|indowError error =`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Fault not found")
@@ -422,17 +476,13 @@ func TestLanguage_findClosestSymbolDeclaration_faults(t *testing.T) {
})
t.Run("Find local fault variable definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
fn void main() {
WindowError error = WindowError.SOMETHING_HAPPENED;
- error = UNEXPECTED_ERROR;
+ e|||rror = UNEXPECTED_ERROR;
}`,
)
- position := buildPosition(4, 5) // Cursor at `e|rror = UNEXPECTED_ERROR``
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Fault not found")
@@ -441,17 +491,13 @@ func TestLanguage_findClosestSymbolDeclaration_faults(t *testing.T) {
})
t.Run("Should find implicit fault constant definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
fn void main() {
WindowError error = WindowError.SOMETHING_HAPPENED;
- error = UNEXPECTED_ERROR;
+ error = U|||NEXPECTED_ERROR;
}`,
)
- position := buildPosition(4, 13) // Cursor at `error = U|NEXPECTED_ERROR;`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
_, ok := symbolOption.Get().(*idx.FaultConstant)
@@ -459,22 +505,71 @@ func TestLanguage_findClosestSymbolDeclaration_faults(t *testing.T) {
assert.Equal(t, "UNEXPECTED_ERROR", symbolOption.Get().GetName())
})
- // TODO Does faults have methods?
+ t.Run("Should not find fault constant on fault constant", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn void main() {
+ WindowError.SOMETHING_HAPPENED.U|||NEXPECTED_ERROR;
+ }`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element found")
+ })
+
+ t.Run("Should not find fault constant on fault instance", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn void main() {
+ WindowError error = WindowError.SOMETHING_HAPPENED;
+ error.U|||NEXPECTED_ERROR;
+ }`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element found")
+ })
+
+ t.Run("Should find fault method definition on instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn bool WindowError.isBad(){}
+
+ fn void main() {
+ WindowError val = UNEXPECTED_ERROR;
+ val.is|||Bad();
+ }
+ `,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Method not found")
+ _, ok := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not a method, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "WindowError.isBad", symbolOption.Get().GetName())
+ })
+
+ t.Run("Should find fault method definition on explicit fault constant", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_ClosestDecl(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn bool WindowError.isBad(){}
+
+ fn void main() {
+ WindowError.UNEXPECTED_ERROR.isB|||ad();
+ }
+ `,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Method not found")
+ _, ok := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not a method, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "WindowError.isBad", symbolOption.Get().GetName())
+ })
}
func TestLanguage_findClosestSymbolDeclaration_def(t *testing.T) {
- state := NewTestState()
- search := NewSearchWithoutLog()
-
t.Run("Find local definition definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`def Kilo = int;
- Kilo value = 3;`,
+ K|||ilo value = 3;`,
)
- position := buildPosition(2, 4) // Cursor at `K|ilo value = 3`
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
assert.Equal(t, "Kilo", symbolOption.Get().GetName())
@@ -482,21 +577,14 @@ func TestLanguage_findClosestSymbolDeclaration_def(t *testing.T) {
}
func TestLanguage_findClosestSymbolDeclaration_functions(t *testing.T) {
- state := NewTestState()
- search := NewSearchWithoutLog()
-
t.Run("Find local function definition", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fn void run(int tick) {
}
fn void main() {
- run(3);
+ r|||un(3);
}`,
)
- position := buildPosition(4, 5) // Cursor at r|un(3);
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
@@ -506,16 +594,12 @@ func TestLanguage_findClosestSymbolDeclaration_functions(t *testing.T) {
})
t.Run("Should not confuse function with virtual root scope function", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fn void main() {
run(3);
}
- fn void call(){ main(); }`,
+ fn void call(){ m|||ain(); }`,
)
- position := buildPosition(4, 20) // Cursor at m|ain();
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
assert.False(t, symbolOption.IsNone(), "Element not found")
@@ -525,18 +609,13 @@ func TestLanguage_findClosestSymbolDeclaration_functions(t *testing.T) {
})
t.Run("Should find function definition without body", func(t *testing.T) {
- state.registerDoc(
- "app.c3",
+ symbolOption := SearchUnderCursor_ClosestDecl(
`fn void init_window(int width, int height, char* title) @extern("InitWindow");
- init_window(200, 200, "hello");
+ i|||nit_window(200, 200, "hello");
`,
)
- position := buildPosition(3, 4) // Cursor at i|nit_window(200, 200, "hello")
-
- symbolOption := search.FindSymbolDeclarationInWorkspace("app.c3", position, &state.state)
-
assert.False(t, symbolOption.IsNone(), "Element not found")
fun := symbolOption.Get().(*idx.Function)
diff --git a/server/internal/lsp/search/search_completion_list.go b/server/internal/lsp/search/search_completion_list.go
index d8621940..3958d3f1 100644
--- a/server/internal/lsp/search/search_completion_list.go
+++ b/server/internal/lsp/search/search_completion_list.go
@@ -151,6 +151,49 @@ func GetCompletionDetail(s symbols.Indexable) *string {
}
}
+// Search for a type's methods.
+func (s *Search) BuildMethodCompletions(
+ state *l.ProjectState,
+ parentTypeFQN string,
+ filterMembers bool,
+ symbolToSearch sourcecode.Word,
+) []protocol.CompletionItem {
+ var items []protocol.CompletionItem
+
+ // Search in enum methods
+ var methods []symbols.Indexable
+ var query string
+ if !filterMembers {
+ query = parentTypeFQN + "."
+ } else {
+ query = parentTypeFQN + "." + symbolToSearch.Text() + "*"
+ }
+
+ replacementRange := protocol_utils.NewLSPRange(
+ uint32(symbolToSearch.PrevAccessPath().TextRange().Start.Line),
+ uint32(symbolToSearch.PrevAccessPath().TextRange().End.Character+1),
+ uint32(symbolToSearch.PrevAccessPath().TextRange().Start.Line),
+ uint32(symbolToSearch.PrevAccessPath().TextRange().End.Character+2),
+ )
+ methods = state.SearchByFQN(query)
+ for _, idx := range methods {
+ fn, _ := idx.(*symbols.Function)
+ kind := idx.GetKind()
+ items = append(items, protocol.CompletionItem{
+ Label: fn.GetName(),
+ Kind: &kind,
+ TextEdit: protocol.TextEdit{
+ NewText: fn.GetMethodName(),
+ Range: replacementRange,
+ },
+ Documentation: GetCompletableDocComment(fn),
+ Detail: GetCompletionDetail(fn),
+ })
+ }
+
+ return items
+}
+
// Returns: []CompletionItem | CompletionList | nil
func (s *Search) BuildCompletionList(
ctx context.CursorContext,
@@ -229,7 +272,7 @@ func (s *Search) BuildCompletionList(
// searchParams.scopeMode = AnyPosition
- prevIndexableOption := s.findParentType(searchParams, state, FindDebugger{depth: 0, enabled: true})
+ membersReadable, prevIndexableOption := s.findParentType(searchParams, state, FindDebugger{depth: 0, enabled: true})
if prevIndexableOption.IsNone() {
return items
}
@@ -241,6 +284,10 @@ func (s *Search) BuildCompletionList(
case *symbols.Struct:
strukt := prevIndexable.(*symbols.Struct)
+ // We don't check for 'membersReadable' here since even variables of structs
+ // can access its members.
+ // TODO: Actually, maybe we should check for NOT membersReadable if it is
+ // impossible to access Struct.member as a type.
for _, member := range strukt.GetMembers() {
if !filterMembers || strings.HasPrefix(member.GetName(), symbolInPosition.Text()) {
items = append(items, protocol.CompletionItem{
@@ -255,68 +302,100 @@ func (s *Search) BuildCompletionList(
}
}
- // Search in struct methods
- var methods []symbols.Indexable
- var query string
- if !filterMembers {
- query = strukt.GetFQN() + "."
- } else {
- query = strukt.GetFQN() + "." + symbolInPosition.Text() + "*"
- }
+ items = append(items, s.BuildMethodCompletions(state, strukt.GetFQN(), filterMembers, symbolInPosition)...)
- replacementRange := protocol_utils.NewLSPRange(
- uint32(symbolInPosition.PrevAccessPath().TextRange().Start.Line),
- uint32(symbolInPosition.PrevAccessPath().TextRange().End.Character+1),
- uint32(symbolInPosition.PrevAccessPath().TextRange().Start.Line),
- uint32(symbolInPosition.PrevAccessPath().TextRange().End.Character+2),
- )
- methods = state.SearchByFQN(query)
- for _, idx := range methods {
- fn, _ := idx.(*symbols.Function)
- kind := idx.GetKind()
- items = append(items, protocol.CompletionItem{
- Label: fn.GetName(),
- Kind: &kind,
- TextEdit: protocol.TextEdit{
- NewText: fn.GetMethodName(),
- Range: replacementRange,
- },
- Documentation: GetCompletableDocComment(fn),
- Detail: GetCompletionDetail(fn),
- })
- }
+ case *symbols.Enumerator:
+ enumerator := prevIndexable.(*symbols.Enumerator)
- case *symbols.Enum:
- enum := prevIndexable.(*symbols.Enum)
- for _, enumerator := range enum.GetEnumerators() {
- if !filterMembers || strings.HasPrefix(enumerator.GetName(), symbolInPosition.Text()) {
+ for _, assoc := range enumerator.AssociatedValues {
+ if !filterMembers || strings.HasPrefix(assoc.GetName(), symbolInPosition.Text()) {
items = append(items, protocol.CompletionItem{
- Label: enumerator.GetName(),
- Kind: &enumerator.Kind,
+ Label: assoc.GetName(),
+ Kind: &assoc.Kind,
- // No documentation for enumerators at this time
+ // No documentation for associated values at this time
Documentation: nil,
- Detail: GetCompletionDetail(enumerator),
+ Detail: GetCompletionDetail(&assoc),
})
}
}
+ // Add parent enum's methods
+ if enumerator.GetModuleString() != "" && enumerator.GetEnumName() != "" {
+ items = append(items, s.BuildMethodCompletions(state, enumerator.GetEnumFQN(), filterMembers, symbolInPosition)...)
+ }
+
+ case *symbols.FaultConstant:
+ constant := prevIndexable.(*symbols.FaultConstant)
+
+ // Add parent fault's methods
+ if constant.GetModuleString() != "" && constant.GetFaultName() != "" {
+ items = append(items, s.BuildMethodCompletions(state, constant.GetFaultFQN(), filterMembers, symbolInPosition)...)
+ }
+
+ case *symbols.Enum:
+ enum := prevIndexable.(*symbols.Enum)
+
+ // Accessing MyEnum.VALUE is ok, but not MyEnum.VALUE.VALUE,
+ // so don't search for enumerators within enumerators
+ // (membersReadable = false).
+ if membersReadable {
+ for _, enumerator := range enum.GetEnumerators() {
+ if !filterMembers || strings.HasPrefix(enumerator.GetName(), symbolInPosition.Text()) {
+ items = append(items, protocol.CompletionItem{
+ Label: enumerator.GetName(),
+ Kind: &enumerator.Kind,
+
+ // No documentation for enumerators at this time
+ Documentation: nil,
+
+ Detail: GetCompletionDetail(enumerator),
+ })
+ }
+ }
+ } else {
+ // This is an enum instance, so we can access associated values.
+ for _, assoc := range enum.GetAssociatedValues() {
+ if !filterMembers || strings.HasPrefix(assoc.GetName(), symbolInPosition.Text()) {
+ items = append(items, protocol.CompletionItem{
+ Label: assoc.GetName(),
+ Kind: &assoc.Kind,
+
+ // No documentation for associated values at this time
+ Documentation: nil,
+
+ Detail: GetCompletionDetail(&assoc),
+ })
+ }
+ }
+ }
+
+ items = append(items, s.BuildMethodCompletions(state, enum.GetFQN(), filterMembers, symbolInPosition)...)
+
case *symbols.Fault:
fault := prevIndexable.(*symbols.Fault)
- for _, constant := range fault.GetConstants() {
- if !filterMembers || strings.HasPrefix(constant.GetName(), symbolInPosition.Text()) {
- items = append(items, protocol.CompletionItem{
- Label: constant.GetName(),
- Kind: &constant.Kind,
-
- // No documentation for fault constants at this time
- Documentation: nil,
- Detail: GetCompletionDetail(constant),
- })
+ // Accessing MyFault.VALUE is ok, but not MyFault.VALUE.VALUE,
+ // so don't search for constants within constants
+ // (membersReadable = false).
+ if membersReadable {
+ for _, constant := range fault.GetConstants() {
+ if !filterMembers || strings.HasPrefix(constant.GetName(), symbolInPosition.Text()) {
+ items = append(items, protocol.CompletionItem{
+ Label: constant.GetName(),
+ Kind: &constant.Kind,
+
+ // No documentation for fault constants at this time
+ Documentation: nil,
+
+ Detail: GetCompletionDetail(constant),
+ })
+ }
}
}
+
+ items = append(items, s.BuildMethodCompletions(state, fault.GetFQN(), filterMembers, symbolInPosition)...)
}
} else {
// Find all symbols in module
@@ -371,10 +450,12 @@ func (s *Search) BuildCompletionList(
return items
}
-func (s *Search) findParentType(searchParams sp.SearchParams, state *l.ProjectState, debugger FindDebugger) option.Option[symbols.Indexable] {
+// Returns whether members can be read from the found symbol, as well as the found symbol itself.
+func (s *Search) findParentType(searchParams sp.SearchParams, state *l.ProjectState, debugger FindDebugger) (bool, option.Option[symbols.Indexable]) {
prevIndexableResult := s.findInParentSymbols(searchParams, state, debugger)
+ membersReadable := prevIndexableResult.membersReadable
if prevIndexableResult.IsNone() {
- return prevIndexableResult.result
+ return membersReadable, prevIndexableResult.result
}
symbolsHierarchy := []symbols.Indexable{}
@@ -385,7 +466,7 @@ func (s *Search) findParentType(searchParams sp.SearchParams, state *l.ProjectSt
prevIndexable = s.resolve(prevIndexable, searchParams.DocId().Get(), searchParams.ModuleInCursor(), state, symbolsHierarchy, debugger)
if prevIndexable == nil {
- return option.None[symbols.Indexable]()
+ return true, option.None[symbols.Indexable]()
}
} else {
break
@@ -408,8 +489,8 @@ func (s *Search) findParentType(searchParams sp.SearchParams, state *l.ProjectSt
prevIndexableResult = s.findClosestSymbolDeclaration(levelSearchParams, state, debugger.goIn())
default:
- return option.Some(prevIndexable)
+ return membersReadable, option.Some(prevIndexable)
}
- return prevIndexableResult.result
+ return membersReadable, prevIndexableResult.result
}
diff --git a/server/internal/lsp/search/search_completion_list_test.go b/server/internal/lsp/search/search_completion_list_test.go
index 67b03ada..c9cd18ff 100644
--- a/server/internal/lsp/search/search_completion_list_test.go
+++ b/server/internal/lsp/search/search_completion_list_test.go
@@ -906,6 +906,210 @@ func TestBuildCompletionList_enums(t *testing.T) {
})
}
})
+
+ t.Run("Should suggest enum associated values", func(t *testing.T) {
+ source := `
+ enum Color : (int assoc, float abc) {
+ RED = { 1, 2.0 },
+ BLUE = { 2, 4.0 }
+ }
+ fn void main() {
+`
+ cases := []struct {
+ name string
+ input string
+ expected []protocol.CompletionItem
+ }{
+ {
+ "Do not find associated values on enum type",
+ "Color.a",
+ []protocol.CompletionItem{}},
+ {
+ "Find associated values on explicit constant",
+ "Color.RED.a",
+ []protocol.CompletionItem{
+ CreateCompletionItem("abc", protocol.CompletionItemKindVariable, "float"),
+ CreateCompletionItem("assoc", protocol.CompletionItemKindVariable, "int"),
+ }},
+
+ {
+ "Find matching associated values on explicit constant",
+ "Color.RED.asso",
+ []protocol.CompletionItem{
+ CreateCompletionItem("assoc", protocol.CompletionItemKindVariable, "int"),
+ }},
+
+ {
+ "Find associated values on enum instance variable",
+ `Color clr = Color.RED;
+clr.a`,
+ []protocol.CompletionItem{
+ CreateCompletionItem("abc", protocol.CompletionItemKindVariable, "float"),
+ CreateCompletionItem("assoc", protocol.CompletionItemKindVariable, "int"),
+ }},
+
+ {
+ "Find matching associated values on enum instance variable",
+ `Color clr = Color.RED;
+clr.asso`,
+ []protocol.CompletionItem{
+ CreateCompletionItem("assoc", protocol.CompletionItemKindVariable, "int"),
+ }},
+ }
+
+ for _, tt := range cases {
+ t.Run(fmt.Sprintf("Autocomplete enum associated values: #%s", tt.name), func(t *testing.T) {
+ state := NewTestState(logger)
+ state.registerDoc("test.c3", source+tt.input+`}`)
+ lines := strings.Split(tt.input, "\n")
+ lastLine := lines[len(lines)-1]
+ position := buildPosition(7+uint(len(lines)-1), uint(len(lastLine))) // Cursor after `|`
+
+ search := NewSearchWithoutLog()
+ completionList := search.BuildCompletionList(
+ context.CursorContext{
+ Position: position,
+ DocURI: "test.c3",
+ },
+ &state.state)
+
+ filtered := filterOutKeywordSuggestions(completionList)
+
+ assert.Equal(t, len(tt.expected), len(filtered))
+ assert.Equal(t, tt.expected, filtered)
+ })
+ }
+ })
+
+ t.Run("Should suggest Enum methods", func(t *testing.T) {
+ source := `
+ enum Color { RED, GREEN, BLUE, COBALT }
+ fn Color Color.transparentize(self) {}
+ fn void main() {
+`
+ cases := []struct {
+ name string
+ input string
+ expected []protocol.CompletionItem
+ }{
+ {
+ "Find enum methods by type name prefix",
+ "Color.",
+ []protocol.CompletionItem{
+ CreateCompletionItem("BLUE", protocol.CompletionItemKindEnumMember, "Enum Value"),
+ CreateCompletionItem("COBALT", protocol.CompletionItemKindEnumMember, "Enum Value"),
+ {
+ Label: "Color.transparentize",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "transparentize",
+ Range: protocol_utils.NewLSPRange(4, 6, 4, 7),
+ },
+ Detail: cast.ToPtr("fn Color(Color self)"),
+ },
+ CreateCompletionItem("GREEN", protocol.CompletionItemKindEnumMember, "Enum Value"),
+ CreateCompletionItem("RED", protocol.CompletionItemKindEnumMember, "Enum Value"),
+ }},
+ {
+ "Find matching enum method by type name prefix",
+ "Color.transpa",
+ []protocol.CompletionItem{
+ {
+ Label: "Color.transparentize",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "transparentize",
+ Range: protocol_utils.NewLSPRange(4, 6, 4, 7),
+ },
+ Detail: cast.ToPtr("fn Color(Color self)"),
+ },
+ },
+ },
+ {
+ "Find enum methods with explicit enum value prefix",
+ "Color.GREEN.",
+ []protocol.CompletionItem{
+ {
+ Label: "Color.transparentize",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "transparentize",
+ Range: protocol_utils.NewLSPRange(4, 12, 4, 13),
+ },
+ Detail: cast.ToPtr("fn Color(Color self)"),
+ },
+ },
+ },
+ {
+ "Find matching enum methods with explicit enum value prefix",
+ "Color.GREEN.transp",
+ []protocol.CompletionItem{
+ {
+ Label: "Color.transparentize",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "transparentize",
+ Range: protocol_utils.NewLSPRange(4, 12, 4, 13),
+ },
+ Detail: cast.ToPtr("fn Color(Color self)"),
+ },
+ },
+ },
+ {
+ "Find enum methods by instance variable prefix",
+ `Color green = Color.GREEN;
+green.`,
+ []protocol.CompletionItem{
+ {
+ Label: "Color.transparentize",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "transparentize",
+ Range: protocol_utils.NewLSPRange(5, 6, 5, 7),
+ },
+ Detail: cast.ToPtr("fn Color(Color self)"),
+ },
+ },
+ },
+ {
+ "Find matching enum method by instance variable prefix",
+ `Color green = Color.GREEN;
+green.transp`,
+ []protocol.CompletionItem{
+ {
+ Label: "Color.transparentize",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "transparentize",
+ Range: protocol_utils.NewLSPRange(5, 6, 5, 7),
+ },
+ Detail: cast.ToPtr("fn Color(Color self)"),
+ },
+ },
+ },
+ }
+
+ for _, tt := range cases {
+ t.Run(fmt.Sprintf("Autocomplete enum methods: #%s", tt.name), func(t *testing.T) {
+ state := NewTestState(logger)
+ state.registerDoc("test.c3", source+tt.input+`}`)
+ lines := strings.Split(tt.input, "\n")
+ lastLine := lines[len(lines)-1]
+ position := buildPosition(5+uint(len(lines)-1), uint(len(lastLine))) // Cursor after `|`
+
+ search := NewSearchWithoutLog()
+ completionList := search.BuildCompletionList(
+ context.CursorContext{
+ Position: position,
+ DocURI: "test.c3",
+ },
+ &state.state)
+
+ assert.Equal(t, len(tt.expected), len(completionList))
+ assert.Equal(t, tt.expected, completionList)
+ })
+ }
+ })
}
func TestBuildCompletionList_faults(t *testing.T) {
@@ -1005,6 +1209,258 @@ func TestBuildCompletionList_faults(t *testing.T) {
})
}
})
+
+ t.Run("Should not suggest Fault constant type after explicit constant", func(t *testing.T) {
+ source := `
+ fault WindowError { COH, COUGH, COUGHCOUGH}
+ fault WindowFileError { NOT_FOUND, NO_PERMISSIONS, COULD_NOT_CREATE }
+ fn void main() {
+`
+ cases := []struct {
+ name string
+ input string
+ expected []protocol.CompletionItem
+ }{
+ {
+ "Do not find constants prefixed with fault constant",
+ "WindowFileError.NOT_FOUND.",
+ nil},
+ {
+ "Do not find matching constants prefixed with fault constant",
+ "WindowFileError.NOT_FOUND.NO_PE",
+ nil},
+ }
+
+ for _, tt := range cases {
+ t.Run(fmt.Sprintf("Autocomplete contants: #%s", tt.name), func(t *testing.T) {
+ state := NewTestState()
+ state.registerDoc("test.c3", source+tt.input+`}`)
+ position := buildPosition(5, uint(len(tt.input))) // Cursor after `|`
+
+ search := NewSearchWithoutLog()
+ completionList := search.BuildCompletionList(
+ context.CursorContext{
+ Position: position,
+ DocURI: "test.c3",
+ },
+ &state.state)
+
+ assert.Equal(t, len(tt.expected), len(completionList))
+ assert.Equal(t, tt.expected, completionList)
+ })
+ }
+ })
+
+ t.Run("Should not suggest Fault constant type after instance", func(t *testing.T) {
+ source := `
+ fault WindowFileError { NOT_FOUND, NO_PERMISSIONS, COULD_NOT_CREATE }
+ fn void main() {
+ WindowFileError inst = NOT_FOUND;
+`
+ cases := []struct {
+ name string
+ input string
+ expected []protocol.CompletionItem
+ }{
+ {
+ "Do not find constants prefixed with fault instance",
+ "inst.",
+ nil},
+ {
+ "Do not find matching constants prefixed with fault instance",
+ "inst.NO_PE",
+ nil},
+ }
+
+ for _, tt := range cases {
+ t.Run(fmt.Sprintf("Autocomplete contants: #%s", tt.name), func(t *testing.T) {
+ state := NewTestState()
+ state.registerDoc("test.c3", source+tt.input+`}`)
+ position := buildPosition(5, uint(len(tt.input))) // Cursor after `|`
+
+ search := NewSearchWithoutLog()
+ completionList := search.BuildCompletionList(
+ context.CursorContext{
+ Position: position,
+ DocURI: "test.c3",
+ },
+ &state.state)
+
+ assert.Equal(t, len(tt.expected), len(completionList))
+ assert.Equal(t, tt.expected, completionList)
+ })
+ }
+ })
+
+ t.Run("Should not suggest Fault constant type after instance in struct member", func(t *testing.T) {
+ source := `
+ fault WindowFileError { NOT_FOUND, NO_PERMISSIONS, COULD_NOT_CREATE }
+ struct MyStruct { WindowFileError f; }
+ fn void main() {
+ MyStruct st = { WindowFileError.NOT_FOUND };
+`
+ cases := []struct {
+ name string
+ input string
+ expected []protocol.CompletionItem
+ }{
+ {
+ "Do not find constants prefixed with fault instance in struct member",
+ "st.f.",
+ nil},
+ {
+ "Do not find matching constants prefixed with fault instance in struct member",
+ "st.f.NO_PE",
+ nil},
+ }
+
+ for _, tt := range cases {
+ t.Run(fmt.Sprintf("Autocomplete contants: #%s", tt.name), func(t *testing.T) {
+ state := NewTestState()
+ state.registerDoc("test.c3", source+tt.input+`}`)
+ position := buildPosition(6, uint(len(tt.input))) // Cursor after `|`
+
+ search := NewSearchWithoutLog()
+ completionList := search.BuildCompletionList(
+ context.CursorContext{
+ Position: position,
+ DocURI: "test.c3",
+ },
+ &state.state)
+
+ assert.Equal(t, len(tt.expected), len(completionList))
+ assert.Equal(t, tt.expected, completionList)
+ })
+ }
+ })
+
+ t.Run("Should suggest Fault methods", func(t *testing.T) {
+ source := `
+ fault WindowError { UNEXPECTED_ERROR, NORMAL_ERROR }
+ fn void WindowError.display(self) {}
+ fn void main() {
+`
+ cases := []struct {
+ name string
+ input string
+ expected []protocol.CompletionItem
+ }{
+ {
+ "Find fault methods by type name prefix",
+ "WindowError.",
+ []protocol.CompletionItem{
+ CreateCompletionItem("NORMAL_ERROR", protocol.CompletionItemKindEnumMember, "Fault Constant"),
+ CreateCompletionItem("UNEXPECTED_ERROR", protocol.CompletionItemKindEnumMember, "Fault Constant"),
+ {
+ Label: "WindowError.display",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "display",
+ Range: protocol_utils.NewLSPRange(4, 12, 4, 13),
+ },
+ Detail: cast.ToPtr("fn void(WindowError self)"),
+ },
+ }},
+ {
+ "Find matching fault method by type name prefix",
+ "WindowError.disp",
+ []protocol.CompletionItem{
+ {
+ Label: "WindowError.display",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "display",
+ Range: protocol_utils.NewLSPRange(4, 12, 4, 13),
+ },
+ Detail: cast.ToPtr("fn void(WindowError self)"),
+ },
+ },
+ },
+ {
+ "Find fault methods with explicit constant prefix",
+ "WindowError.UNEXPECTED_ERROR.",
+ []protocol.CompletionItem{
+ {
+ Label: "WindowError.display",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "display",
+ Range: protocol_utils.NewLSPRange(4, 29, 4, 30),
+ },
+ Detail: cast.ToPtr("fn void(WindowError self)"),
+ },
+ },
+ },
+ {
+ "Find matching fault methods with explicit constant prefix",
+ "WindowError.UNEXPECTED_ERROR.disp",
+ []protocol.CompletionItem{
+ {
+ Label: "WindowError.display",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "display",
+ Range: protocol_utils.NewLSPRange(4, 29, 4, 30),
+ },
+ Detail: cast.ToPtr("fn void(WindowError self)"),
+ },
+ },
+ },
+ {
+ "Find fault methods by instance variable prefix",
+ `WindowError e = WindowError.UNEXPECTED_ERROR;
+e.`,
+ []protocol.CompletionItem{
+ {
+ Label: "WindowError.display",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "display",
+ Range: protocol_utils.NewLSPRange(5, 2, 5, 3),
+ },
+ Detail: cast.ToPtr("fn void(WindowError self)"),
+ },
+ },
+ },
+ {
+ "Find matching fault methods by instance variable prefix",
+ `WindowError e = WindowError.UNEXPECTED_ERROR;
+e.disp`,
+ []protocol.CompletionItem{
+ {
+ Label: "WindowError.display",
+ Kind: cast.ToPtr(protocol.CompletionItemKindMethod),
+ TextEdit: protocol.TextEdit{
+ NewText: "display",
+ Range: protocol_utils.NewLSPRange(5, 2, 5, 3),
+ },
+ Detail: cast.ToPtr("fn void(WindowError self)"),
+ },
+ },
+ },
+ }
+
+ for _, tt := range cases {
+ t.Run(fmt.Sprintf("Autocomplete enumerables: #%s", tt.name), func(t *testing.T) {
+ state := NewTestState()
+ state.registerDoc("test.c3", source+tt.input+`}`)
+ lines := strings.Split(tt.input, "\n")
+ lastLine := lines[len(lines)-1]
+ position := buildPosition(5+uint(len(lines)-1), uint(len(lastLine))) // Cursor after `|`
+
+ search := NewSearchWithoutLog()
+ completionList := search.BuildCompletionList(
+ context.CursorContext{
+ Position: position,
+ DocURI: "test.c3",
+ },
+ &state.state)
+
+ assert.Equal(t, len(tt.expected), len(completionList))
+ assert.Equal(t, tt.expected, completionList)
+ })
+ }
+ })
}
func TestBuildCompletionList_modules(t *testing.T) {
diff --git a/server/internal/lsp/search/search_find_access_path_test.go b/server/internal/lsp/search/search_find_access_path_test.go
index a0513a15..d7d366d5 100644
--- a/server/internal/lsp/search/search_find_access_path_test.go
+++ b/server/internal/lsp/search/search_find_access_path_test.go
@@ -10,36 +10,34 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
+func SearchUnderCursor_AccessPath(body string, optionalState ...TestState) SearchResult {
state := NewTestState()
search := NewSearchWithoutLog()
- t.Run("Should find enumerator with path definition", func(t *testing.T) {
- state.registerDoc(
- "enums.c3",
- `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
- WindowStatus stat = WindowStatus.OPEN;`,
- )
- position := buildPosition(2, 37) // Cursor at `WindowStatus stat = WindowStatus.O|PEN;`
- doc := state.GetDoc("enums.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(
- &doc,
- *state.state.GetUnitModulesByDoc(doc.URI),
- position,
- )
+ if len(optionalState) > 0 {
+ state = optionalState[0]
+ }
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
+ cursorlessBody, position := parseBodyWithCursor(body)
+ state.registerDoc(
+ "app.c3",
+ cursorlessBody,
+ )
- assert.False(t, symbolOption.IsNone(), "Element not found")
- _, ok := symbolOption.Get().(*idx.Enumerator)
- assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not an enumerator, %s was found", reflect.TypeOf(symbolOption.Get())))
- assert.Equal(t, "OPEN", symbolOption.Get().GetName())
- })
+ doc := state.GetDoc("app.c3")
+ searchParams := search_params.BuildSearchBySymbolUnderCursor(
+ &doc,
+ *state.state.GetUnitModulesByDoc(doc.URI),
+ position,
+ )
+ return search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
+}
+
+func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
t.Run("Should find method from std collection", func(t *testing.T) {
state := NewTestStateWithStdLibVersion("0.5.5")
- state.registerDoc(
- "def.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`module core::actions;
import std::collections::map;
@@ -48,18 +46,10 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
ActionListMap actionLists;
}
fn void ActionListManager.addActionList(&self, ActionList actionList) {
- self.actionLists.set(actionList.getName(), actionList);
+ self.actionLists.s|||et(actionList.getName(), actionList);
}`,
+ state,
)
- position := buildPosition(9, 22) // Cursor at `self.actionLists.s|et(actionList.getName(), actionList);`
- doc := state.GetDoc("def.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(
- &doc,
- *state.state.GetUnitModulesByDoc(doc.URI),
- position,
- )
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, symbolOption.IsNone(), "Element not found")
fun := symbolOption.Get().(*idx.Function)
@@ -67,16 +57,10 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
t.Run("Should find fault constant definition with path definition", func(t *testing.T) {
- state.registerDoc(
- "faults.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
- WindowError error = WindowError.SOMETHING_HAPPENED;`,
+ WindowError error = WindowError.S|||OMETHING_HAPPENED;`,
)
- position := buildPosition(2, 36) // Cursor at `WindowError error = WindowError.S|OMETHING_HAPPENED;`
- doc := state.GetDoc("faults.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, symbolOption.IsNone(), "Element not found")
_, ok := symbolOption.Get().(*idx.FaultConstant)
@@ -85,21 +69,15 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
t.Run("Should find local struct member variable definition", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
bool on;
}
Emu emulator;
- emulator.on = true;`,
+ emulator.o|||n = true;`,
)
- position := buildPosition(7, 13) // Cursor at `emulator.o|n = true`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -110,24 +88,17 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
t.Run("Should find local struct member variable definition when struct is a pointer", func(t *testing.T) {
- state.clearDocs()
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
bool on;
}
fn void Emu.run(Emu* emu) {
- emu.on = true;
+ emu.o|||n = true;
emu.tick();
}`,
)
- position := buildPosition(7, 9) // Cursor at emulator.o|n = true
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -140,20 +111,14 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
// This test maybe works better in language_find_closes_declaration_test.go
t.Run("Should find same struct member declaration, when cursor is already in member declaration", func(t *testing.T) {
t.Skip() // Do not understand this test.
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`Cpu cpu; // Trap for finding struct member when cursor is on declaration member.
struct Emu {
- Cpu cpu;
+ Cpu c|||pu;
Audio audio;
bool on;
}`,
)
- position := buildPosition(12, 8) // Cursor at `Cpu c|pu;`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -164,28 +129,22 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
t.Run("Should find same struct member declaration, when struct is behind a def and cursor is already in member declaration", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`
struct Camera3D {
int target;
}
- def Camera = Camera3D;
-
+ def Camera = Camera3D;
+
struct Widget {
int count;
Camera camera;
}
-
+
Widget view = {};
- view.camera.target = 3;
+ view.camera.t|||arget = 3;
`,
)
- position := buildPosition(13, 16) // Cursor at `view.camera.t|arget = 3;`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, symbolOption.IsNone(), "Symbol not found")
symbol := symbolOption.Get()
@@ -196,8 +155,7 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
t.Run("Should find struct method", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
@@ -206,22 +164,17 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
fn void Emu.init(Emu* emu) {}
fn void main() {
Emu emulator;
- emulator.init();
+ emulator.i|||nit();
}`,
)
- // Cursor at `emulator.i|nit();`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), buildPosition(9, 14))
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
fun := symbolOption.Get().(*idx.Function)
assert.Equal(t, "Emu.init", fun.GetName())
assert.Equal(t, "Emu.init", fun.GetFullName())
})
t.Run("Should find struct method on alternative callable", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ resolvedSymbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
@@ -230,42 +183,32 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
fn void Emu.init(Emu* emu) {}
fn void main() {
Emu emulator;
- Emu.init(&emulator);
+ Emu.i|||nit(&emulator);
}`,
)
- // Cursor at `Emu.i|nit(&emulator);`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), buildPosition(9, 9))
- resolvedSymbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
fun := resolvedSymbolOption.Get().(*idx.Function)
assert.Equal(t, "Emu.init", fun.GetName())
assert.Equal(t, "Emu.init", fun.GetFullName())
})
t.Run("Should find struct method when cursor is already in method declaration", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ resolvedSymbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
bool on;
}
- fn void Emu.init(Emu* emu) {}`,
+ fn void Emu.i|||nit(Emu* emu) {}`,
)
- // Cursor at `Emu.i|nit();`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), buildPosition(6, 16))
- resolvedSymbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
fun := resolvedSymbolOption.Get().(*idx.Function)
assert.Equal(t, "Emu.init", fun.GetName())
assert.Equal(t, "Emu.init", fun.GetFullName())
})
t.Run("Should find struct member when cursor is on chained returned from function", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ resolvedSymbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
@@ -276,22 +219,17 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
return emulator;
}
fn void main() {
- newEmu().on = false;
+ newEmu().o|||n = false;
}`,
)
- // Cursor at `newEmu().o|n = false;`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), buildPosition(11, 14))
- resolvedSymbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
variable := resolvedSymbolOption.Get().(*idx.StructMember)
assert.Equal(t, "on", variable.GetName())
assert.Equal(t, "bool", variable.GetType().GetName())
})
t.Run("Should find struct method when cursor is on chained returned from function", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ resolvedSymbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
@@ -303,40 +241,30 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
}
fn void Emu.init(){}
fn void main() {
- newEmu().init();
+ newEmu().i|||nit();
}`,
)
- // Cursor at `newEmu().i|nit();`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), buildPosition(12, 14))
- resolvedSymbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
fun := resolvedSymbolOption.Get().(*idx.Function)
assert.Equal(t, "Emu.init", fun.GetName())
assert.Equal(t, "Emu.init", fun.GetFullName())
})
t.Run("Should find local struct method when there are N nested structs", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ resolvedSymbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
bool on;
}
fn void Emu.init(Emu* emu) {
- emu.audio.init();
+ emu.audio.i|||nit();
}
struct Audio {
int frequency;
}
fn void Audio.init() {}`,
)
- position := buildPosition(7, 15) // Cursor at `emu.audio.i|nit();``
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- resolvedSymbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.False(t, resolvedSymbolOption.IsNone(), "Struct method not found")
@@ -347,52 +275,41 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
t.Run("Should find struct method on alternative callable when there are N nested structs", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
bool on;
}
fn void Emu.init(Emu* emu) {
- Audio.init(&emu.audio);
+ Audio.i|||nit(&emu.audio);
}
struct Audio {
int frequency;
}
fn void Audio.init() {}`,
)
- // Cursor at `Audio.i|nit(&emu.audio);`
- doc := state.GetDoc("structs.c3")
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), buildPosition(7, 11))
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
fun := symbolOption.Get().(*idx.Function)
assert.Equal(t, "Audio.init", fun.GetName())
assert.Equal(t, "Audio.init", fun.GetFullName())
})
t.Run("Should not find local struct method definition", func(t *testing.T) {
- state.registerDoc(
- "structs.c3",
+ symbolOption := SearchUnderCursor_AccessPath(
`struct Emu {
Cpu cpu;
Audio audio;
bool on;
}
fn void Emu.init(Emu* emu) {
- emu.audio.unknown();
+ emu.audio.u|||nknown();
}
struct Audio {
int frequency;
}
fn void Audio.init() {}`,
)
- doc := state.GetDoc("structs.c3")
- position := buildPosition(7, 15) // Cursor is at emu.audio.u|nknown
- searchParams := search_params.BuildSearchBySymbolUnderCursor(&doc, *state.state.GetUnitModulesByDoc(doc.URI), position)
-
- symbolOption := search.findClosestSymbolDeclaration(searchParams, &state.state, debugger)
assert.True(t, symbolOption.IsNone(), "Struct method should not be found")
})
@@ -417,6 +334,285 @@ func TestProjectState_findClosestSymbolDeclaration_access_path(t *testing.T) {
})
}
+func TestProjectState_findClosestSymbolDeclaration_access_path_enums(t *testing.T) {
+ t.Run("Should find enumerator with path definition", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ WindowStatus stat = WindowStatus.O|||PEN;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ _, ok := symbolOption.Get().(*idx.Enumerator)
+ assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not an enumerator, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "OPEN", symbolOption.Get().GetName())
+ })
+
+ t.Run("Should not find enumerator after explicit enumerator path", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ WindowStatus stat = WindowStatus.OPEN.B|||ACKGROUND;`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element was found")
+ })
+
+ t.Run("Should not find enumerator after instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ WindowStatus stat = WindowStatus.OPEN;
+ WindoWStatus bad = stat.B|||ACKGROUND;`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Element was found")
+ })
+
+ t.Run("Should find enum method on instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ fn bool WindowStatus.isOpen() {}
+ fn void main() {
+ WindowStatus val = WindowStatus.OPEN;
+ val.is|||Open();
+ }`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowStatus.isOpen", fun.GetName())
+ assert.Equal(t, "WindowStatus.isOpen", fun.GetFullName())
+ })
+
+ t.Run("Should find enum method on explicit enumerator", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ fn bool WindowStatus.isOpen() {}
+ fn void main() {
+ WindowStatus.OPEN.i|||sOpen();
+ }`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowStatus.isOpen", fun.GetName())
+ assert.Equal(t, "WindowStatus.isOpen", fun.GetFullName())
+ })
+
+ t.Run("Should find associated value on explicit enumerator", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus : int (int assoc) {
+ OPEN = 5,
+ BACKGROUND = 6,
+ MINIMIZED = 7
+ }
+ int stat = WindowStatus.OPEN.a|||ssoc;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a variable, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "assoc", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should find associated value on explicit enumerator without custom backing type", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus : (int assoc) {
+ OPEN = 5,
+ BACKGROUND = 6,
+ MINIMIZED = 7
+ }
+ int stat = WindowStatus.OPEN.a|||ssoc;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a variable, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "assoc", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should find associated value on enum instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus : (int assoc) {
+ OPEN = 5,
+ BACKGROUND = 6,
+ MINIMIZED = 7
+ }
+ WindowStatus stat = WindowStatus.OPEN;
+ int val = stat.a|||ssoc;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a variable, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "assoc", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should find enumerator on def-aliased enum", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ def AliasStatus = WindowStatus;
+ WindowStatus stat = AliasStatus.O|||PEN;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ _, ok := symbolOption.Get().(*idx.Enumerator)
+ assert.Equal(t, true, ok, fmt.Sprintf("The symbol is not an enumerator, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "OPEN", symbolOption.Get().GetName())
+ })
+
+ t.Run("Should find associated value on def-aliased instance", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus : (int assoc) {
+ OPEN = 5,
+ BACKGROUND = 6,
+ MINIMIZED = 7
+ }
+ WindowStatus stat = WindowStatus.OPEN;
+ def alias_stat = stat;
+ int val = alias_stat.a|||ssoc;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ variable, ok := symbolOption.Get().(*idx.Variable)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a variable, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "assoc", variable.GetName())
+ assert.Equal(t, "int", variable.GetType().GetName())
+ })
+
+ t.Run("Should find enum method on def-aliased global variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `enum WindowStatus { OPEN, BACKGROUND, MINIMIZED }
+ WindowStatus stat = WindowStatus.OPEN;
+ def aliased_stat = stat;
+ fn bool WindowStatus.isOpen() {}
+ fn void main() {
+ aliased_stat.is|||Open();
+ }`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowStatus.isOpen", fun.GetName())
+ assert.Equal(t, "WindowStatus.isOpen", fun.GetFullName())
+ })
+}
+
+func TestProjectState_findClosestSymbolDeclaration_access_path_faults(t *testing.T) {
+ t.Run("Should find fault constant with path definition", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ WindowError err = WindowError.U|||NEXPECTED_ERROR;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ _, ok := symbolOption.Get().(*idx.FaultConstant)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a fault constant, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "UNEXPECTED_ERROR", symbolOption.Get().GetName())
+ })
+
+ t.Run("Should not find fault constant after explicit instance", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ WindowError err = WindowError.UNEXPECTED_ERROR.S|||OMETHING_HAPPENED;`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Constant was wrongly found on instance")
+ })
+
+ t.Run("Should not find fault constant after instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ WindowError err = WindowError.UNEXPECTED_ERROR;
+ WindowError bad = err.S|||OMETHING_HAPPENED;`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Constant was wrongly found on instance variable")
+ })
+
+ t.Run("Should not find fault constant after instance variable in struct member", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ struct MyStruct { WindowError f; }
+ MyStruct st = { WindowError.UNEXPECTED_ERROR };
+ WindowError bad = st.f.S|||OMETHING_HAPPENED;`,
+ )
+
+ assert.True(t, symbolOption.IsNone(), "Constant was wrongly found on instance variable")
+ })
+
+ t.Run("Should find fault method on instance variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn bool WindowError.isBad() {}
+ fn void main() {
+ WindowError val = WindowError.UNEXPECTED_ERROR;
+ val.is|||Bad();
+ }`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowError.isBad", fun.GetName())
+ assert.Equal(t, "WindowError.isBad", fun.GetFullName())
+ })
+
+ t.Run("Should find fault method after instance variable in struct member", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn bool WindowError.isBad() {}
+ struct MyStruct { WindowError f; }
+ MyStruct st = { WindowError.UNEXPECTED_ERROR };
+ WindowError bad = st.f.i|||sBad();`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowError.isBad", fun.GetName())
+ assert.Equal(t, "WindowError.isBad", fun.GetFullName())
+ })
+
+ t.Run("Should find fault method on explicit constant", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ fn bool WindowError.isBad() {}
+ fn void main() {
+ WindowError.UNEXPECTED_ERROR.is|||Bad();
+ }`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowError.isBad", fun.GetName())
+ assert.Equal(t, "WindowError.isBad", fun.GetFullName())
+ })
+
+ t.Run("Should find fault constant on def-aliased fault", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ WindowError constant = WindowError.UNEXPECTED_ERROR;
+ def AliasedFault = WindowError;
+ WindowError value = AliasedFault.U|||NEXPECTED_ERROR;`,
+ )
+
+ assert.False(t, symbolOption.IsNone(), "Element not found")
+ _, ok := symbolOption.Get().(*idx.FaultConstant)
+ assert.True(t, ok, fmt.Sprintf("The symbol is not a fault constant, %s was found", reflect.TypeOf(symbolOption.Get())))
+ assert.Equal(t, "UNEXPECTED_ERROR", symbolOption.Get().GetName())
+ })
+
+ t.Run("Should find fault method on def-aliased global variable", func(t *testing.T) {
+ symbolOption := SearchUnderCursor_AccessPath(
+ `fault WindowError { UNEXPECTED_ERROR, SOMETHING_HAPPENED }
+ WindowError constant = WindowError.UNEXPECTED_ERROR;
+ def ct_alias = constant;
+ fn bool WindowError.isBad() {}
+ fn void main() {
+ ct_alias.is|||Bad();
+ }`,
+ )
+
+ fun := symbolOption.Get().(*idx.Function)
+ assert.Equal(t, "WindowError.isBad", fun.GetName())
+ assert.Equal(t, "WindowError.isBad", fun.GetFullName())
+ })
+}
+
func TestProjectState_findClosestSymbolDeclaration_access_path_with_generics(t *testing.T) {
state := NewTestState()
search := NewSearchWithoutLog()
@@ -426,7 +622,7 @@ func TestProjectState_findClosestSymbolDeclaration_access_path_with_generics(t *
"app.c3",
`module app;
import list;
-
+
struct Home {
List() rooms;
}
diff --git a/server/internal/lsp/search/search_result.go b/server/internal/lsp/search/search_result.go
index db6cd656..c5ff5e52 100644
--- a/server/internal/lsp/search/search_result.go
+++ b/server/internal/lsp/search/search_result.go
@@ -7,9 +7,15 @@ import (
type TrackedModules map[string]int
type SearchResult struct {
- result option.Option[symbols.Indexable]
- //trackedModules map[string]int
+ // Whether the members of the found indexable are readable.
+ // This is `true` if we were searching for a type and got a type, so its
+ // members can be accessed, as well as its methods.
+ // This is `false` if we were searching for a variable and got a type, so
+ // that variable's type cannot be accessed, only its methods.
+ membersReadable bool
+ result option.Option[symbols.Indexable]
traversedModules map[string]bool
+ //trackedModules map[string]int
}
func (s SearchResult) TraversedModules() map[string]bool {
@@ -24,10 +30,18 @@ func (s SearchResult) IsNone() bool {
return s.result.IsNone()
}
+func (s *SearchResult) AreMembersReadable() bool {
+ return s.membersReadable
+}
+
func (s SearchResult) Get() symbols.Indexable {
return s.result.Get()
}
+func (s *SearchResult) SetMembersReadable(membersReadable bool) {
+ s.membersReadable = membersReadable
+}
+
func (s *SearchResult) Set(symbol symbols.Indexable) {
s.result = option.Some(symbol)
}
@@ -70,6 +84,7 @@ func NewSearchResult(trackedModules TrackedModules) SearchResult {
func NewSearchResultEmptyWithTraversedModules(traversedModules map[string]bool) SearchResult {
return SearchResult{
+ membersReadable: true,
result: option.None[symbols.Indexable](),
traversedModules: traversedModules,
}
@@ -81,6 +96,18 @@ func _NewSearchResult(result option.Option[symbols.Indexable], trackedModules Tr
traversedModules[moduleName] = true
}
return SearchResult{
+ // Default members readable to 'true' as, usually, we're searching for the type
+ // itself rather than a variable with that type. On a few cases, however, we
+ // convert a variable into its type to search for more information. In those cases,
+ // we should explicitly set `membersReadable` as appropriate, but only at the
+ // conversion step. After advancing further into the access chain, if we do so,
+ // for example, `membersReadable` would not necessarily remain `false`, as it just
+ // refers to the immediate result.
+ //
+ // That is, `CoolEnum.VALUE` works because `CoolEnum` would imply a `membersReadable: true`
+ // search, but `CoolEnum.VALUE.VALUE2` wouldn't work as `CoolEnum.VALUE` would imply
+ // `membersReadable: false`.
+ membersReadable: true,
result: option.None[symbols.Indexable](),
traversedModules: traversedModules,
}
diff --git a/server/internal/lsp/search/search_test.go b/server/internal/lsp/search/search_test.go
index 7ecefa46..d67137cd 100644
--- a/server/internal/lsp/search/search_test.go
+++ b/server/internal/lsp/search/search_test.go
@@ -1,10 +1,14 @@
package search
import (
+ "strings"
+
"github.com/pherrymason/c3-lsp/internal/lsp/project_state"
"github.com/pherrymason/c3-lsp/pkg/document"
"github.com/pherrymason/c3-lsp/pkg/option"
p "github.com/pherrymason/c3-lsp/pkg/parser"
+ "github.com/pherrymason/c3-lsp/pkg/symbols"
+ "github.com/pherrymason/c3-lsp/pkg/utils"
"github.com/tliron/commonlog"
)
@@ -75,6 +79,30 @@ func (s *TestState) registerDoc(docId string, source string) {
s.state.RefreshDocumentIdentifiers(&doc, &s.parser)
}
+func buildPosition(line uint, character uint) symbols.Position {
+ return symbols.Position{Line: line - 1, Character: character}
+}
+
+// Parses a test body with a '|||' cursor, returning the body without
+// the cursor and the position of that cursor.
+//
+// Useful for tests where we check what the language server responds if the
+// user cursor is at a certain position.
+func parseBodyWithCursor(body string) (string, symbols.Position) {
+ cursorLine, cursorCol := utils.FindLineColOfSubstring(body, "|||")
+ if cursorLine == 0 {
+ panic("Please add the cursor position to the test body with '|||'")
+ }
+ if strings.Count(body, "|||") > 1 {
+ panic("There are multiple '|||' cursors in the test body, please add only one")
+ }
+
+ cursorlessBody := strings.ReplaceAll(body, "|||", "")
+ position := buildPosition(cursorLine, cursorCol)
+
+ return cursorlessBody, position
+}
+
func createParser() p.Parser {
logger := &commonlog.MockLogger{}
return p.NewParser(logger)
diff --git a/server/internal/lsp/stdlib/v055.go b/server/internal/lsp/stdlib/v055.go
index 1e175d6a..3a8cb30e 100644
--- a/server/internal/lsp/stdlib/v055.go
+++ b/server/internal/lsp/stdlib/v055.go
@@ -8,11 +8,11 @@ import (
func Load_v055_stdlib() symbolstable.UnitModules {
docId := "_stdlib"
moduleCollection := map[string]*symbols.Module{
- "libc": symbols.NewModuleBuilder("libc", "/libc/libc.c3").WithoutSourceCode().Build(),
+ "libc": symbols.NewModuleBuilder("libc", "/libc/os/posix.c3").WithoutSourceCode().Build(),
"libc::errno": symbols.NewModuleBuilder("libc::errno", "/libc/libc.c3").WithoutSourceCode().Build(),
"libc::os": symbols.NewModuleBuilder("libc::os", "/libc/os/errno.c3").WithoutSourceCode().Build(),
"std::ascii": symbols.NewModuleBuilder("std::ascii", "/ascii.c3").WithoutSourceCode().Build(),
- "std::atomic": symbols.NewModuleBuilder("std::atomic", "/atomic_nolibc.c3").WithoutSourceCode().Build(),
+ "std::atomic": symbols.NewModuleBuilder("std::atomic", "/atomic.c3").WithoutSourceCode().Build(),
"std::atomic::types": symbols.NewModuleBuilder("std::atomic::types", "/atomic.c3").WithoutSourceCode().Build(),
"std::bits": symbols.NewModuleBuilder("std::bits", "/bits.c3").WithoutSourceCode().Build(),
"std::collections::bitset": symbols.NewModuleBuilder("std::collections::bitset", "/collections/bitset.c3").WithoutSourceCode().Build(),
@@ -42,9 +42,9 @@ func Load_v055_stdlib() symbolstable.UnitModules {
"std::core::env": symbols.NewModuleBuilder("std::core::env", "/core/env.c3").WithoutSourceCode().Build(),
"std::core::main_stub": symbols.NewModuleBuilder("std::core::main_stub", "/core/private/main_stub.c3").WithoutSourceCode().Build(),
"std::core::mem": symbols.NewModuleBuilder("std::core::mem", "/core/mem.c3").WithoutSourceCode().Build(),
- "std::core::mem::allocator": symbols.NewModuleBuilder("std::core::mem::allocator", "/core/allocators/tracking_allocator.c3").WithoutSourceCode().Build(),
+ "std::core::mem::allocator": symbols.NewModuleBuilder("std::core::mem::allocator", "/core/allocators/arena_allocator.c3").WithoutSourceCode().Build(),
"std::core::runtime": symbols.NewModuleBuilder("std::core::runtime", "/core/runtime.c3").WithoutSourceCode().Build(),
- "std::core::string": symbols.NewModuleBuilder("std::core::string", "/core/string_to_real.c3").WithoutSourceCode().Build(),
+ "std::core::string": symbols.NewModuleBuilder("std::core::string", "/core/string.c3").WithoutSourceCode().Build(),
"std::core::string::conv": symbols.NewModuleBuilder("std::core::string::conv", "/core/conv.c3").WithoutSourceCode().Build(),
"std::core::string::iterator": symbols.NewModuleBuilder("std::core::string::iterator", "/core/string_iterator.c3").WithoutSourceCode().Build(),
"std::core::types": symbols.NewModuleBuilder("std::core::types", "/core/types.c3").WithoutSourceCode().Build(),
@@ -60,38 +60,38 @@ func Load_v055_stdlib() symbolstable.UnitModules {
"std::hash::fnv32a": symbols.NewModuleBuilder("std::hash::fnv32a", "/hash/fnv32a.c3").WithoutSourceCode().Build(),
"std::hash::fnv64a": symbols.NewModuleBuilder("std::hash::fnv64a", "/hash/fnv64a.c3").WithoutSourceCode().Build(),
"std::hash::sha1": symbols.NewModuleBuilder("std::hash::sha1", "/hash/sha1.c3").WithoutSourceCode().Build(),
- "std::io": symbols.NewModuleBuilder("std::io", "/io/stream/scanner.c3").WithoutSourceCode().Build(),
+ "std::io": symbols.NewModuleBuilder("std::io", "/io/formatter_private.c3").WithoutSourceCode().Build(),
"std::io::file": symbols.NewModuleBuilder("std::io::file", "/io/file.c3").WithoutSourceCode().Build(),
- "std::io::os": symbols.NewModuleBuilder("std::io::os", "/io/os/file_nolibc.c3").WithoutSourceCode().Build(),
+ "std::io::os": symbols.NewModuleBuilder("std::io::os", "/io/os/chdir.c3").WithoutSourceCode().Build(),
"std::io::path": symbols.NewModuleBuilder("std::io::path", "/io/path.c3").WithoutSourceCode().Build(),
- "std::math": symbols.NewModuleBuilder("std::math", "/math/math_i128.c3").WithoutSourceCode().Build(),
+ "std::math": symbols.NewModuleBuilder("std::math", "/math/math_libc.c3").WithoutSourceCode().Build(),
"std::math::complex": symbols.NewModuleBuilder("std::math::complex", "/math/math_complex.c3").WithoutSourceCode().Build(),
"std::math::easing": symbols.NewModuleBuilder("std::math::easing", "/math/math_easings.c3").WithoutSourceCode().Build(),
"std::math::matrix": symbols.NewModuleBuilder("std::math::matrix", "/math/math_matrix.c3").WithoutSourceCode().Build(),
- "std::math::nolibc": symbols.NewModuleBuilder("std::math::nolibc", "/math/math_nolibc/round.c3").WithoutSourceCode().Build(),
+ "std::math::nolibc": symbols.NewModuleBuilder("std::math::nolibc", "/math/math_nolibc/__cos.c3").WithoutSourceCode().Build(),
"std::math::quaternion": symbols.NewModuleBuilder("std::math::quaternion", "/math/math_quaternion.c3").WithoutSourceCode().Build(),
- "std::math::random": symbols.NewModuleBuilder("std::math::random", "/math/random/math.mcg.c3").WithoutSourceCode().Build(),
+ "std::math::random": symbols.NewModuleBuilder("std::math::random", "/math/random/math.seeder.c3").WithoutSourceCode().Build(),
"std::math::vector": symbols.NewModuleBuilder("std::math::vector", "/math/math_vector.c3").WithoutSourceCode().Build(),
- "std::net": symbols.NewModuleBuilder("std::net", "/net/socket.c3").WithoutSourceCode().Build(),
- "std::net::os": symbols.NewModuleBuilder("std::net::os", "/net/os/darwin.c3").WithoutSourceCode().Build(),
+ "std::net": symbols.NewModuleBuilder("std::net", "/net/inetaddr.c3").WithoutSourceCode().Build(),
+ "std::net::os": symbols.NewModuleBuilder("std::net::os", "/net/os/win32.c3").WithoutSourceCode().Build(),
"std::net::tcp": symbols.NewModuleBuilder("std::net::tcp", "/net/tcp.c3").WithoutSourceCode().Build(),
"std::net::udp": symbols.NewModuleBuilder("std::net::udp", "/net/udp.c3").WithoutSourceCode().Build(),
"std::os": symbols.NewModuleBuilder("std::os", "/os/cpu.c3").WithoutSourceCode().Build(),
"std::os::backtrace": symbols.NewModuleBuilder("std::os::backtrace", "/os/backtrace.c3").WithoutSourceCode().Build(),
- "std::os::darwin": symbols.NewModuleBuilder("std::os::darwin", "/os/macos/darwin.c3").WithoutSourceCode().Build(),
+ "std::os::darwin": symbols.NewModuleBuilder("std::os::darwin", "/os/macos/heap.c3").WithoutSourceCode().Build(),
"std::os::env": symbols.NewModuleBuilder("std::os::env", "/os/env.c3").WithoutSourceCode().Build(),
"std::os::linux": symbols.NewModuleBuilder("std::os::linux", "/os/linux/heap.c3").WithoutSourceCode().Build(),
- "std::os::macos::cf": symbols.NewModuleBuilder("std::os::macos::cf", "/os/macos/core_foundation.c3").WithoutSourceCode().Build(),
+ "std::os::macos::cf": symbols.NewModuleBuilder("std::os::macos::cf", "/os/macos/cf_allocator.c3").WithoutSourceCode().Build(),
"std::os::macos::objc": symbols.NewModuleBuilder("std::os::macos::objc", "/os/macos/objc.c3").WithoutSourceCode().Build(),
- "std::os::posix": symbols.NewModuleBuilder("std::os::posix", "/os/posix/files.c3").WithoutSourceCode().Build(),
+ "std::os::posix": symbols.NewModuleBuilder("std::os::posix", "/os/posix/heap.c3").WithoutSourceCode().Build(),
"std::os::process": symbols.NewModuleBuilder("std::os::process", "/os/subprocess.c3").WithoutSourceCode().Build(),
- "std::os::win32": symbols.NewModuleBuilder("std::os::win32", "/os/win32/process.c3").WithoutSourceCode().Build(),
+ "std::os::win32": symbols.NewModuleBuilder("std::os::win32", "/os/win32/types.c3").WithoutSourceCode().Build(),
"std::os::win32::wsa": symbols.NewModuleBuilder("std::os::win32::wsa", "/os/win32/wsa.c3").WithoutSourceCode().Build(),
- "std::sort": symbols.NewModuleBuilder("std::sort", "/sort/binarysearch.c3").WithoutSourceCode().Build(),
+ "std::sort": symbols.NewModuleBuilder("std::sort", "/sort/quicksort.c3").WithoutSourceCode().Build(),
"std::sort::qs": symbols.NewModuleBuilder("std::sort::qs", "/sort/quicksort.c3").WithoutSourceCode().Build(),
"std::thread": symbols.NewModuleBuilder("std::thread", "/threads/thread.c3").WithoutSourceCode().Build(),
"std::thread::cpu": symbols.NewModuleBuilder("std::thread::cpu", "/threads/os/cpu.c3").WithoutSourceCode().Build(),
- "std::thread::os": symbols.NewModuleBuilder("std::thread::os", "/threads/os/thread_win32.c3").WithoutSourceCode().Build(),
+ "std::thread::os": symbols.NewModuleBuilder("std::thread::os", "/os/posix/threads.c3").WithoutSourceCode().Build(),
"std::thread::pool": symbols.NewModuleBuilder("std::thread::pool", "/threads/pool.c3").WithoutSourceCode().Build(),
"std::time": symbols.NewModuleBuilder("std::time", "/time/time.c3").WithoutSourceCode().Build(),
"std::time::clock": symbols.NewModuleBuilder("std::time::clock", "/time/clock.c3").WithoutSourceCode().Build(),
@@ -106,11 +106,11 @@ func Load_v055_stdlib() symbolstable.UnitModules {
// Define module std::ascii
module = moduleCollection["std::ascii"]
- module.AddFunction(symbols.NewFunctionBuilder("in_range_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("start", "", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("len", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_lower_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_upper_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_digit_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_bdigit_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_odigit_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_xdigit_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alpha_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_print_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_graph_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_space_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alnum_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_punct_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_blank_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_cntrl_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_lower_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_upper_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("in_range", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("start", "char", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("len", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_lower", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_upper", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_digit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_bdigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_odigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_xdigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alpha", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_print", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_graph", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_space", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alnum", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_punct", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_blank", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_cntrl", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_lower", symbols.NewTypeFromString("char", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_upper", symbols.NewTypeFromString("char", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("in_range", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("start", "char", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("len", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_lower", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_upper", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_digit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_bdigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_odigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_xdigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alpha", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_print", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_graph", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_space", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alnum", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_punct", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_blank", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_cntrl", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_lower", symbols.NewTypeFromString("char", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_upper", symbols.NewTypeFromString("char", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("from_hex", symbols.NewTypeFromString("char", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("char").WithArgument(symbols.NewVariableBuilder("c", "char", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("in_range", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("start", "uint", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("len", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_lower", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_upper", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_digit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_bdigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_odigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_xdigit", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alpha", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_print", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_graph", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_space", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alnum", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_punct", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_blank", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_cntrl", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_lower", symbols.NewTypeFromString("uint", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_upper", symbols.NewTypeFromString("uint", "std::ascii"), "std::ascii", "/ascii.c3").WithTypeIdentifier("uint").WithArgument(symbols.NewVariableBuilder("c", "uint", "std::ascii", "/ascii.c3").Build()).WithoutSourceCode().Build())
+ module.AddFunction(symbols.NewFunctionBuilder("in_range_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("start", "", "std::ascii", "/ascii.c3").Build()).WithArgument(symbols.NewVariableBuilder("len", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_lower_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_upper_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_digit_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_bdigit_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_odigit_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_xdigit_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alpha_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_print_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_graph_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_space_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_alnum_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_punct_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_blank_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("is_cntrl_m", symbols.NewTypeFromString("bool", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_lower_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "/ascii.c3").Build()).IsMacro().WithoutSourceCode().Build()).AddFunction(symbols.NewFunctionBuilder("to_upper_m", symbols.NewTypeFromString("", "std::ascii"), "std::ascii", "/ascii.c3").WithArgument(symbols.NewVariableBuilder("c", "", "std::ascii", "