Skip to content

Commit ae3e877

Browse files
committed
[vscode] Go to implementations.
fusionlanguage#64
1 parent 2e48817 commit ae3e877

File tree

9 files changed

+148
-153
lines changed

9 files changed

+148
-153
lines changed

AST.fu

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,7 @@ public class FuMethod : FuMethodBase
12461246
}
12471247
public override bool IsStatic() => this.CallType == FuCallType.Static;
12481248
public bool IsAbstractOrVirtual() => this.CallType == FuCallType.Abstract || this.CallType == FuCallType.Virtual;
1249+
public bool IsAbstractVirtualOrOverride() => this.CallType == FuCallType.Abstract || this.CallType == FuCallType.Virtual || this.CallType == FuCallType.Override;
12491250
public FuVar!? FirstParameter()
12501251
{
12511252
assert this.Parameters.First is FuVar! first; // FIXME: FuVar!?

GenC.fu

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,11 +1813,8 @@ public class GenC : GenCCpp
18131813
WriteUpcast(declaringClass, klass.Parent);
18141814
}
18151815
else {
1816-
FuClass definingClass = declaringClass;
1817-
switch (method.CallType) {
1818-
case FuCallType.Abstract:
1819-
case FuCallType.Virtual:
1820-
case FuCallType.Override:
1816+
if (method.IsAbstractVirtualOrOverride()) {
1817+
FuClass definingClass = declaringClass;
18211818
if (method.CallType == FuCallType.Override) {
18221819
assert method.GetDeclaringMethod().Parent is FuClass declaringClass1;
18231820
declaringClass = declaringClass1;
@@ -1842,11 +1839,9 @@ public class GenC : GenCCpp
18421839
WriteChar(')');
18431840
Write("->");
18441841
WriteCamelCase(method.Name);
1845-
break;
1846-
default:
1847-
WriteName(method);
1848-
break;
18491842
}
1843+
else
1844+
WriteName(method);
18501845
WriteChar('(');
18511846
if (method.CallType != FuCallType.Static) {
18521847
if (obj != null)

Sema.fu

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,20 +2228,13 @@ public class FuSema
22282228
if (method.CallType == FuCallType.Override || method.CallType == FuCallType.Sealed) {
22292229
if (klass.Parent.TryLookup(method.Name, false) is FuMethod! baseMethod) {
22302230
// TODO: check private
2231-
switch (baseMethod.CallType) {
2232-
case FuCallType.Abstract:
2233-
case FuCallType.Virtual:
2234-
case FuCallType.Override:
2235-
if (method.IsMutator() != baseMethod.IsMutator()) {
2236-
if (method.IsMutator())
2237-
ReportError(method, "Mutating method cannot override a non-mutating method");
2238-
else
2239-
ReportError(method, "Non-mutating method cannot override a mutating method");
2240-
}
2241-
break;
2242-
default:
2231+
if (!baseMethod.IsAbstractVirtualOrOverride())
22432232
ReportError(method, "Base method is not abstract or virtual");
2244-
break;
2233+
else if (method.IsMutator() != baseMethod.IsMutator()) {
2234+
if (method.IsMutator())
2235+
ReportError(method, "Mutating method cannot override a non-mutating method");
2236+
else
2237+
ReportError(method, "Non-mutating method cannot override a mutating method");
22452238
}
22462239
if (!method.Type.EqualsType(baseMethod.Type))
22472240
ReportError(method.Type, "Base method has a different return type");

editors/vscode/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ A Visual Studio Code extension for the [Fusion programming language](https://fus
22

33
* Syntax highlighting
44
* Error highlighting
5-
* Outline
6-
* Go to definition
7-
* Go to references, Find all references
5+
* Breadcrumbs, Outline
6+
* Go to / Peek definition
7+
* Go to / Peek / Find all implementations
8+
* Go to / Peek / Find all references
89
* Snippets
910

1011
Fusion is a language designed to be translated automatically to

editors/vscode/src/extension.ts

Lines changed: 73 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// along with Fusion Transpiler. If not, see http://www.gnu.org/licenses/
2020

2121
import * as vscode from "vscode";
22-
import { FuParser, FuProgram, FuSystem, FuSema, FuSemaHost, FuSymbolReferenceVisitor, FuStatement, FuSymbol, FuContainerType, FuEnum, FuMember, FuField, FuMethod } from "./fucheck.js";
22+
import { FuParser, FuProgram, FuSystem, FuSema, FuSemaHost, FuSymbolReferenceVisitor, FuStatement, FuSymbol, FuContainerType, FuEnum, FuClass, FuMember, FuField, FuMethod } from "./fucheck.js";
2323

2424
class VsCodeHost extends FuSemaHost
2525
{
@@ -73,29 +73,6 @@ class VsCodeHost extends FuSemaHost
7373
sema.setHost(this);
7474
sema.process();
7575
}
76-
77-
async findSymbol(document: vscode.TextDocument, position: vscode.Position): Promise<FuSymbol | null>
78-
{
79-
const parser = this.createParser();
80-
const filename = document.uri.toString();
81-
parser.findName(filename, position.line, position.character);
82-
const files = await vscode.workspace.findFiles("*.fu");
83-
if (files.some(uri => uri.toString() == filename))
84-
await this.parseFolder(files, parser);
85-
else
86-
this.parseDocument(document, parser);
87-
this.doSema();
88-
return parser.getFoundDefinition();
89-
}
90-
91-
toLocation(statement: FuStatement): vscode.Location | null
92-
{
93-
if (statement.loc <= 0)
94-
return null;
95-
const line = this.program.getLine(statement.loc);
96-
const file = this.program.getSourceFile(line);
97-
return new vscode.Location(vscode.Uri.parse(file.filename), new vscode.Position(line - file.line, statement.loc - this.program.lineLocs[line]));
98-
}
9976
}
10077

10178
class VsCodeDiagnostics extends VsCodeHost
@@ -159,39 +136,87 @@ class VsCodeDiagnostics extends VsCodeHost
159136
}
160137
}
161138

162-
class VsCodeDefinitionProvider extends VsCodeHost
163-
{
164-
async findDefinition(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location | null>
165-
{
166-
const symbol = await this.findSymbol(document, position);
167-
return symbol == null ? null : this.toLocation(symbol);
168-
}
169-
}
170-
171139
class VsCodeReferenceCollector extends FuSymbolReferenceVisitor
172140
{
173-
host: VsCodeHost;
141+
provider: VsCodeGotoProvider;
174142
result: vscode.Location[] = [];
175143

176-
constructor(host: VsCodeHost)
144+
constructor(provider: VsCodeGotoProvider)
177145
{
178146
super();
179-
this.host = host;
147+
this.provider = provider;
180148
}
181149

182150
visitFound(reference: FuStatement): void
183151
{
184-
const location = this.host.toLocation(reference);
185-
if (location != null)
186-
this.result.push(location);
152+
this.provider.pushLocation(this.result, reference);
187153
}
188154
}
189155

190-
class VsCodeReferenceProvider extends VsCodeHost
156+
class VsCodeGotoProvider extends VsCodeHost
191157
{
158+
async #findSymbol(document: vscode.TextDocument, position: vscode.Position): Promise<FuSymbol | null>
159+
{
160+
const parser = this.createParser();
161+
const filename = document.uri.toString();
162+
parser.findName(filename, position.line, position.character);
163+
const files = await vscode.workspace.findFiles("*.fu");
164+
if (files.some(uri => uri.toString() == filename))
165+
await this.parseFolder(files, parser);
166+
else
167+
this.parseDocument(document, parser);
168+
this.doSema();
169+
return parser.getFoundDefinition();
170+
}
171+
172+
#toLocation(statement: FuStatement): vscode.Location | null
173+
{
174+
if (statement.loc <= 0)
175+
return null;
176+
const line = this.program.getLine(statement.loc);
177+
const file = this.program.getSourceFile(line);
178+
return new vscode.Location(vscode.Uri.parse(file.filename), new vscode.Position(line - file.line, statement.loc - this.program.lineLocs[line]));
179+
}
180+
181+
pushLocation(result: vscode.Location[], statement: FuStatement): void
182+
{
183+
const location = this.#toLocation(statement);
184+
if (location != null)
185+
result.push(location);
186+
}
187+
188+
async findDefinition(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location | null>
189+
{
190+
const symbol = await this.#findSymbol(document, position);
191+
return symbol == null ? null : this.#toLocation(symbol);
192+
}
193+
194+
async findImplementations(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location[]>
195+
{
196+
const symbol = await this.#findSymbol(document, position);
197+
const result: vscode.Location[] = [];
198+
if (symbol != null) {
199+
if (symbol instanceof FuClass) {
200+
for (const subclass of this.program.classes) {
201+
if (symbol.isSameOrBaseOf(subclass))
202+
this.pushLocation(result, subclass);
203+
}
204+
}
205+
else if (symbol instanceof FuMethod && symbol.isAbstractVirtualOrOverride()) {
206+
for (const subclass of this.program.classes) {
207+
if (symbol.parent.isSameOrBaseOf(subclass) && subclass.contains(symbol))
208+
this.pushLocation(result, subclass.tryLookup(symbol.name, false));
209+
}
210+
}
211+
else
212+
this.pushLocation(result, symbol);
213+
}
214+
return result;
215+
}
216+
192217
async findReferences(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location[]>
193218
{
194-
const symbol = await this.findSymbol(document, position);
219+
const symbol = await this.#findSymbol(document, position);
195220
if (symbol == null)
196221
return [];
197222
const collector = new VsCodeReferenceCollector(this);
@@ -250,12 +275,17 @@ export function activate(context: vscode.ExtensionContext): void
250275
context.subscriptions.push(vscode.workspace.onDidCloseTextDocument(document => diagnostics.delete(document)));
251276
vscode.languages.registerDefinitionProvider("fusion", {
252277
provideDefinition(document, position, token) {
253-
return new VsCodeDefinitionProvider().findDefinition(document, position);
278+
return new VsCodeGotoProvider().findDefinition(document, position);
279+
}
280+
});
281+
vscode.languages.registerImplementationProvider("fusion", {
282+
provideImplementation(document, position, token) {
283+
return new VsCodeGotoProvider().findImplementations(document, position);
254284
}
255285
});
256286
vscode.languages.registerReferenceProvider("fusion", {
257287
provideReferences(document, position, context, token) {
258-
return new VsCodeReferenceProvider().findReferences(document, position);
288+
return new VsCodeGotoProvider().findReferences(document, position);
259289
}
260290
});
261291
vscode.languages.registerDocumentSymbolProvider("fusion", {

libfut.cpp

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,11 @@ bool FuMethod::isAbstractOrVirtual() const
22702270
return this->callType == FuCallType::abstract || this->callType == FuCallType::virtual_;
22712271
}
22722272

2273+
bool FuMethod::isAbstractVirtualOrOverride() const
2274+
{
2275+
return this->callType == FuCallType::abstract || this->callType == FuCallType::virtual_ || this->callType == FuCallType::override_;
2276+
}
2277+
22732278
FuVar * FuMethod::firstParameter() const
22742279
{
22752280
FuVar * first = static_cast<FuVar *>(this->parameters.first);
@@ -6650,20 +6655,13 @@ void FuSema::resolveCode(FuClass * klass)
66506655
if (method->body != nullptr) {
66516656
if (method->callType == FuCallType::override_ || method->callType == FuCallType::sealed) {
66526657
if (FuMethod *baseMethod = dynamic_cast<FuMethod *>(klass->parent->tryLookup(method->name, false).get())) {
6653-
switch (baseMethod->callType) {
6654-
case FuCallType::abstract:
6655-
case FuCallType::virtual_:
6656-
case FuCallType::override_:
6657-
if (method->isMutator() != baseMethod->isMutator()) {
6658-
if (method->isMutator())
6659-
reportError(method, "Mutating method cannot override a non-mutating method");
6660-
else
6661-
reportError(method, "Non-mutating method cannot override a mutating method");
6662-
}
6663-
break;
6664-
default:
6658+
if (!baseMethod->isAbstractVirtualOrOverride())
66656659
reportError(method, "Base method is not abstract or virtual");
6666-
break;
6660+
else if (method->isMutator() != baseMethod->isMutator()) {
6661+
if (method->isMutator())
6662+
reportError(method, "Mutating method cannot override a non-mutating method");
6663+
else
6664+
reportError(method, "Non-mutating method cannot override a mutating method");
66676665
}
66686666
if (!method->type->equalsType(baseMethod->type.get()))
66696667
reportError(method->type.get(), "Base method has a different return type");
@@ -11067,42 +11065,35 @@ void GenC::writeCCall(const FuExpr * obj, const FuMethod * method, const std::ve
1106711065
writeUpcast(declaringClass, klass->parent);
1106811066
}
1106911067
else {
11070-
const FuClass * definingClass = declaringClass;
11071-
switch (method->callType) {
11072-
case FuCallType::abstract:
11073-
case FuCallType::virtual_:
11074-
case FuCallType::override_:
11068+
if (method->isAbstractVirtualOrOverride()) {
11069+
const FuClass * definingClass = declaringClass;
1107511070
if (method->callType == FuCallType::override_) {
1107611071
const FuClass * declaringClass1 = static_cast<const FuClass *>(method->getDeclaringMethod()->parent);
1107711072
declaringClass = declaringClass1;
1107811073
}
1107911074
if (obj != nullptr)
1108011075
klass = obj->type->asClassType()->class_;
11081-
{
11082-
const FuClass * ptrClass = getVtblPtrClass(klass);
11083-
const FuClass * structClass = getVtblStructClass(definingClass);
11084-
if (structClass != ptrClass) {
11085-
write("((const ");
11086-
writeName(structClass);
11087-
write("Vtbl *) ");
11088-
}
11089-
if (obj != nullptr) {
11090-
obj->accept(this, FuPriority::primary);
11091-
writeMemberAccess(obj->type.get(), ptrClass);
11092-
}
11093-
else
11094-
writeSelfForField(ptrClass);
11095-
write("vtbl");
11096-
if (structClass != ptrClass)
11097-
writeChar(')');
11098-
write("->");
11099-
writeCamelCase(method->name);
11100-
break;
11076+
const FuClass * ptrClass = getVtblPtrClass(klass);
11077+
const FuClass * structClass = getVtblStructClass(definingClass);
11078+
if (structClass != ptrClass) {
11079+
write("((const ");
11080+
writeName(structClass);
11081+
write("Vtbl *) ");
1110111082
}
11102-
default:
11103-
writeName(method);
11104-
break;
11083+
if (obj != nullptr) {
11084+
obj->accept(this, FuPriority::primary);
11085+
writeMemberAccess(obj->type.get(), ptrClass);
11086+
}
11087+
else
11088+
writeSelfForField(ptrClass);
11089+
write("vtbl");
11090+
if (structClass != ptrClass)
11091+
writeChar(')');
11092+
write("->");
11093+
writeCamelCase(method->name);
1110511094
}
11095+
else
11096+
writeName(method);
1110611097
writeChar('(');
1110711098
if (method->callType != FuCallType::static_) {
1110811099
if (obj != nullptr)

0 commit comments

Comments
 (0)