Skip to content

Commit

Permalink
DRAFT enable go to definition
Browse files Browse the repository at this point in the history
- Adjusted how linenbr is stored in tokens... allowing line number for each copy of same named local token
- Adds definition provider
  • Loading branch information
ironsheep committed Oct 29, 2023
1 parent fa3ff63 commit 3354eee
Show file tree
Hide file tree
Showing 13 changed files with 374 additions and 58 deletions.
6 changes: 4 additions & 2 deletions spin2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@ Possible next additions:
- Add new-file templates as Snippets
- Add additional Snippets as the community identifies them

## [2.2.0] 2023-10-28

- Awaken **Show Definitions of a Symbol feature** supporting go-to definition. (returns one or more matching symbols found in current open find and included objects)

## [2.1.0] 2023-10-27

Formal release of Language-server-based P1 and P2 Spin Extension for VScode


- Files included by current file are parsed and references to the included objects are validated
- Documentation from the included object files is shown for Hover Text and Signature help
- Live parsing on file change allowing changes in one editor window to affect another editor window (e.g., You have a toplevel spin file open and and object spin file open in 2nd window. Changes in object file can immediately affect the toplevel file.)
- Display of errors found during parse are listed for each file parsed (and with **Error Lens** errors show on affected line)
- What a file is parsed and errors are found the file entry in the left panel file browser turns light red to highlight that file contains errors
- Many improvements in parsing / highlighting for both P1 and P2


## [2.0.0 - 2.0.4] 2023 Oct 22-26

Convert to Spin and Spin2 Language Server as separate Process (P1 and P2)
Expand Down
9 changes: 8 additions & 1 deletion spin2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This Extension is continually in development. Things may, occasionally, not work

This extension provides support for P1 (spin and pasm) along with P2 (Spin2 and Pasm2), the primary languages for programming P1 [Parallax Propeller 1 or P8X32A](https://www.parallax.com/propeller-1/) and the P2 [Parallax Propeller2 or P2X8C4M64P](https://propeller.parallax.com/p2.html)

We've moved to a **Language Server based extension** so that we can awaken **multi-file behaviors** such as show help from included file in top-level file when hovering or showing signature help. This also applies to upoming features such as go to definition.
We've moved to a **Language Server based extension** so that we can awaken **multi-file behaviors** such as show help from included file in top-level file when hovering or showing signature help. This also applies to upoming features such as go to definition.

All features provided by this extension support both the Parallax Propeller 1 and Propeller 2 languages: Spin and Pasm.

Expand Down Expand Up @@ -79,6 +79,13 @@ Help With Method Signatures displays information about the method that is being
- If your own methods are not yet documented, the this signature help still supports entry of the parameter values as well as reminds you how to add your own documentation for your PUB and PRI methods.
- When the method being entered is from an included object the help text is brought in from the external included object.

## Feature: Show Definitions of a Symbol

Allow the user to see/go to the definition of variables/methods right where the variables/methods are being used.

- Enables right-mouse commands "Go to Definition" and "Peek -> Peek Definition"
- In spin this works for method names, global variables, parameters, return values, method local variables and pasm global labels.

## Feature: Generate "Object public interface" documentation

Upon pressing Ctrl+Alt+d (control alt document) the editor will now generate a `{filename}.txt` document file (for your `{filename}.spin2` or `{filename}.spin` file) and open it up to the right side of your editor window. The generator extracts all PUB methods and their doc-comments along with file-top and file-bottom doc-comments.
Expand Down
2 changes: 1 addition & 1 deletion spin2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "P1 and P2 Spin/Pasm Syntax/Semantic Highlighting w/Code Outline, Object Outline and Custom tabbing support",
"author": "IronSheep",
"license": "MIT",
"version": "2.1.0",
"version": "2.2.0",
"repository": {
"type": "git",
"url": "https://github.com/ironsheep/P2-vscode-langserv-extension"
Expand Down
6 changes: 3 additions & 3 deletions spin2/server/src/DocumentProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export class ProcessedDocument {
}
}

export type ProcessedDocumentByURI = Map<string, ProcessedDocument>;
export type DocumentFindingsByURI = Map<string, DocumentFindings>;
export type ProcessedDocumentByFSpec = Map<string, ProcessedDocument>;
export type DocumentFindingsByFSpec = Map<string, DocumentFindings>;
export type TopDocsByFSpec = Map<string, string>;

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -203,7 +203,7 @@ export default class DocumentProcessor {
//
let tmpFindingsForDocument: DocumentFindings | undefined = this.ctx.findingsByFSpec.get(docFSpec);
if (!tmpFindingsForDocument) {
tmpFindingsForDocument = new DocumentFindings();
tmpFindingsForDocument = new DocumentFindings(document.uri);
tmpFindingsForDocument.setFilename(docFSpec);
this.ctx.findingsByFSpec.set(docFSpec, tmpFindingsForDocument);
this.ctx.logger.log(`TRC: ADD Findings: ${tmpFindingsForDocument.instanceName()}`);
Expand Down
6 changes: 3 additions & 3 deletions spin2/server/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// src/context.ts

import * as lsp from "vscode-languageserver";
import { ProcessedDocumentByURI, DocumentFindingsByURI, TopDocsByFSpec } from "./DocumentProcessor";
import { ProcessedDocumentByFSpec, DocumentFindingsByFSpec, TopDocsByFSpec } from "./DocumentProcessor";

//import Parser from "web-tree-sitter";
//import path from "path";
Expand All @@ -18,8 +18,8 @@ export class ServerBehaviorConfiguration {

export interface Context {
topDocsByFSpec: TopDocsByFSpec;
docsByFSpec: ProcessedDocumentByURI;
findingsByFSpec: DocumentFindingsByURI;
docsByFSpec: ProcessedDocumentByFSpec;
findingsByFSpec: DocumentFindingsByFSpec;
workspaceFolders: lsp.WorkspaceFolder[];
language: string;
logger: lsp.Logger;
Expand Down
2 changes: 1 addition & 1 deletion spin2/server/src/parser/spin.objectReferenceParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ export class Spin2ObjectReferenceParser {
}
if (enumConstant.charAt(0).match(/[a-zA-Z_]/)) {
this._logCON(" -- GLBL enumConstant=[" + enumConstant + "]");
this.semanticFindings.setGlobalToken(enumConstant, new RememberedToken("enumMember", ["readonly"]), lineNbr, undefined);
this.semanticFindings.setGlobalToken(enumConstant, new RememberedToken("enumMember", lineNbr - 1, ["readonly"]), undefined);
}
}
}
Expand Down
136 changes: 121 additions & 15 deletions spin2/server/src/parser/spin.semantic.findings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Range, DiagnosticSeverity, SymbolKind, Diagnostic } from "vscode-langua
import { displayEnumByTypeName } from "./spin2.utils";
import { eDebugDisplayType } from "./spin.common";
import { Context } from "../context";
import { Position } from "vscode-languageserver-textdocument";

// ============================================================================
// this file contains objects we use in tracking symbol use and declaration
Expand Down Expand Up @@ -36,6 +37,12 @@ enum eCommentFilter {
allComments,
}

export interface ILocationOfToken {
uri: string;
objectName: string;
position: Position; // if more detail desired in future capture and return token offset into line!
}

export interface IBlockSpan {
startLineIdx: number;
endLineIdx: number;
Expand Down Expand Up @@ -137,8 +144,12 @@ export class DocumentFindings {
// tracking includes
private objectFilenameByInstanceName = new Map<string, string>();
private ctx: Context | undefined;
private docUri: string = "--uri-not-set--";

public constructor() {
public constructor(documentUri: string | undefined = undefined) {
if (documentUri) {
this.docUri = documentUri;
}
if (this.findingsLogEnabled) {
if (this.bLogStarted == false) {
this.bLogStarted = true;
Expand All @@ -158,6 +169,11 @@ export class DocumentFindings {
this.methodLocalPasmTokens = new NameScopedTokenSet("methPasmTOK");
}

public get uri(): string {
// property: URI for doc of these findings
return this.docUri;
}

public setFilename(filespec: string): void {
// append filespec to our instance number
const orignalId: string = this.instanceId;
Expand Down Expand Up @@ -380,6 +396,68 @@ export class DocumentFindings {
return symbolsInNamespace;
}

public getNamespaces(): string[] {
// return list of object namespaces found in toplevel
const nameSpaceSet: string[] = Array.from(this.objectParseResultByObjectName.keys());
return nameSpaceSet;
}

public locationsOfToken(tokenName: string): ILocationOfToken[] {
const desiredLocations: ILocationOfToken[] = [];
this.appendLocationsOfToken(tokenName, desiredLocations, "top");
this._logMessage(` -- locationsOfToken() id=[${this.instanceId}] returns ${desiredLocations.length} tokens`);
return desiredLocations;
}

public appendLocationsOfToken(tokenName: string, locationsSoFar: ILocationOfToken[], objectName: string) {
let referenceDetails: RememberedToken | undefined = undefined;
const desiredTokenKey: string = tokenName.toLowerCase();
let findCount: number = 0;
// get global token from this objects
if (this.isGlobalToken(tokenName)) {
referenceDetails = this.getGlobalToken(tokenName);
if (referenceDetails) {
const tokenPosition: Position = { line: referenceDetails.lineIndex, character: 0 };
const tokenRef: ILocationOfToken = { uri: this.uri, objectName: objectName, position: tokenPosition };
locationsSoFar.push(tokenRef);
findCount++;
this._logMessage(` -- appLoc-Token FOUND global token=[${tokenName}]`);
} else {
this._logMessage(` -- appLoc-Token global token=[${tokenName}] has NO lineNbr info!`);
}
}
if (this.isLocalToken(tokenName)) {
// get local tokens from this objects
const referenceSet: RememberedToken[] = this.getLocalTokens(tokenName);
for (let index = 0; index < referenceSet.length; index++) {
referenceDetails = referenceSet[index];
if (referenceDetails) {
const tokenPosition: Position = { line: referenceDetails.lineIndex, character: 0 };
const tokenRef: ILocationOfToken = { uri: this.uri, objectName: objectName, position: tokenPosition };
locationsSoFar.push(tokenRef);
findCount++;
this._logMessage(` -- appLoc-Token FOUND local token=[${tokenName}]`);
} else {
this._logMessage(` -- appLoc-Token local token=[${tokenName}] has NO lineNbr info!`);
}
}
}
const referencedObjects: string[] = this.getNamespaces();
// get global/local tokens from all included objects
for (let index = 0; index < referencedObjects.length; index++) {
const nameSpace = referencedObjects[index];
const symbolsFound: DocumentFindings | undefined = this.getFindingsForNamespace(nameSpace);

if (symbolsFound) {
if (this.ctx) {
symbolsFound.enableLogging(this.ctx, this.findingsLogEnabled);
}
symbolsFound.appendLocationsOfToken(tokenName, locationsSoFar, nameSpace);
}
}
this._logMessage(` -- appendLocationsOfToken() id=[${this.instanceId}] adds ${findCount} tokens`);
}

// -------------------------------------------------------------------------------------
// TRACK ranges of CON/PUB/PRI/VAR/DAT/OBJ blocks within file
//
Expand Down Expand Up @@ -657,7 +735,7 @@ export class DocumentFindings {
const displayInfo: IDebugDisplayInfo = this.getDebugDisplayInfoForUserName(tokenName);
if (displayInfo.eDisplayType != eDebugDisplayType.Unknown) {
// we have a debug display type!
findings.token = new RememberedToken("debugDisplay", [displayInfo.displayTypeString]);
findings.token = new RememberedToken("debugDisplay", displayInfo.lineNbr - 1, [displayInfo.displayTypeString]);
findings.scope = "Global";
findings.tokenRawInterp = "Global: " + this._rememberdTokenString(tokenName, findings.token);
const termType: string = displayInfo.displayTypeString.toUpperCase();
Expand Down Expand Up @@ -935,12 +1013,12 @@ export class DocumentFindings {
this._logMessage(` `);
}

public setGlobalToken(tokenName: string, token: RememberedToken, declarationLineNumber: number, declarationComment: string | undefined, reference?: string | undefined): void {
public setGlobalToken(tokenName: string, token: RememberedToken, declarationComment: string | undefined, reference?: string | undefined): void {
if (!this.isGlobalToken(tokenName)) {
this._logMessage(" -- NEW-gloTOK " + this._rememberdTokenString(tokenName, token) + `, ln#${declarationLineNumber}, cmt=[${declarationComment}], ref=[${reference}]`);
this._logMessage(" -- NEW-gloTOK " + this._rememberdTokenString(tokenName, token) + `, ln#${token.lineIndex + 1}, cmt=[${declarationComment}], ref=[${reference}]`);
this.globalTokens.setToken(tokenName, token);
// and remember declataion line# for this token
const newDescription: RememberedTokenDeclarationInfo = new RememberedTokenDeclarationInfo(declarationLineNumber - 1, declarationComment, reference);
const newDescription: RememberedTokenDeclarationInfo = new RememberedTokenDeclarationInfo(token.lineIndex, declarationComment, reference);
const desiredTokenKey: string = tokenName.toLowerCase();
this.declarationInfoByGlobalTokenName.set(desiredTokenKey, newDescription);
}
Expand All @@ -952,12 +1030,27 @@ export class DocumentFindings {
// let's never return a declaration modifier! (somehow declaration creeps in to our list!??)
//let modifiersNoDecl: string[] = this._modifiersWithout(desiredToken.modifiers, "declaration");
let modifiersNoDecl: string[] = desiredToken.modifiersWithout("declaration");
desiredToken = new RememberedToken(desiredToken.type, modifiersNoDecl);
desiredToken = new RememberedToken(desiredToken.type, desiredToken.lineIndex, modifiersNoDecl);
this._logMessage(" -- FND-gloTOK " + this._rememberdTokenString(tokenName, desiredToken));
}
return desiredToken;
}

public getLocalTokens(tokenName: string): RememberedToken[] {
const desiredTokens: RememberedToken[] = [];
if (this.isLocalToken(tokenName)) {
const methodNameKeys: string[] = this.methodLocalTokens.keys();
for (let index = 0; index < methodNameKeys.length; index++) {
const methodName = methodNameKeys[index];
const tokenForMethod: RememberedToken | undefined = this.getLocalTokenForMethod(tokenName, methodName);
if (tokenForMethod) {
desiredTokens.push(tokenForMethod);
}
}
}
return desiredTokens;
}

public isLocalToken(tokenName: string): boolean {
const foundStatus: boolean = this.methodLocalTokens.hasToken(tokenName);
this._logMessage(` -- IS-locTOK [${tokenName}] says ${foundStatus}`);
Expand All @@ -969,13 +1062,13 @@ export class DocumentFindings {
return foundStatus;
}

public setLocalTokenForMethod(methodName: string, tokenName: string, token: RememberedToken, declarationLineNumber: number, declarationComment: string | undefined): void {
public setLocalTokenForMethod(methodName: string, tokenName: string, token: RememberedToken, declarationComment: string | undefined): void {
if (!this.isLocalTokenForMethod(methodName, tokenName)) {
this._logMessage(` -- NEW-locTOK ln#${declarationLineNumber} method=[${methodName}], ` + this._rememberdTokenString(tokenName, token) + `, cmt=[${declarationComment}]`);
this._logMessage(` -- NEW-locTOK ln#${token.lineIndex + 1} method=[${methodName}], ` + this._rememberdTokenString(tokenName, token) + `, cmt=[${declarationComment}]`);
this.methodLocalTokens.setTokenForMethod(methodName, tokenName, token);
// and remember declataion line# for this token
const desiredTokenKey: string = tokenName.toLowerCase();
this.declarationInfoByLocalTokenName.set(desiredTokenKey, new RememberedTokenDeclarationInfo(declarationLineNumber - 1, declarationComment));
this.declarationInfoByLocalTokenName.set(desiredTokenKey, new RememberedTokenDeclarationInfo(token.lineIndex, declarationComment));
}
}

Expand All @@ -996,6 +1089,11 @@ export class DocumentFindings {
return desiredToken;
}

private getLocalTokenForMethod(tokenName: string, methodName: string): RememberedToken | undefined {
const desiredToken: RememberedToken | undefined = this.methodLocalTokens.getTokenForMethod(methodName, tokenName);
return desiredToken;
}

public startMethod(methodName: string, lineNbr: number): void {
// starting a new method remember the name and assoc the line number
if (this.currMethodName) {
Expand Down Expand Up @@ -1058,15 +1156,15 @@ export class DocumentFindings {
return foundStatus;
}

public setLocalPAsmTokenForMethod(methodName: string, tokenName: string, token: RememberedToken, declarationLineNumber: number, declarationComment: string | undefined): void {
public setLocalPAsmTokenForMethod(methodName: string, tokenName: string, token: RememberedToken, declarationComment: string | undefined): void {
if (this.hasLocalPAsmTokenForMethod(methodName, tokenName)) {
// WARNING attempt to set again
} else {
// set new one!
this.methodLocalPasmTokens.setTokenForMethod(methodName, tokenName, token);
// and remember declataion line# for this token
const desiredTokenKey: string = tokenName.toLowerCase();
this.declarationInfoByLocalTokenName.set(desiredTokenKey, new RememberedTokenDeclarationInfo(declarationLineNumber - 1, declarationComment));
this.declarationInfoByLocalTokenName.set(desiredTokenKey, new RememberedTokenDeclarationInfo(token.lineIndex, declarationComment));
const newToken = this.methodLocalPasmTokens.getTokenForMethod(methodName, tokenName);
if (newToken) {
this._logMessage(" -- NEW-lpTOK method=" + methodName + ": " + this._rememberdTokenString(tokenName, newToken));
Expand Down Expand Up @@ -1283,7 +1381,7 @@ export class TokenSet {
// let's never return a declaration modifier! (somehow "declaration" creeps in to our list!??)
//let modifiersNoDecl: string[] = this._modifiersWithout(desiredToken.modifiers, "declaration");
let modifiersNoDecl: string[] = desiredToken.modifiersWithout("declaration");
desiredToken = new RememberedToken(desiredToken.type, modifiersNoDecl);
desiredToken = new RememberedToken(desiredToken.type, desiredToken._lineIdx, modifiersNoDecl);
}
return desiredToken;
}
Expand Down Expand Up @@ -1329,8 +1427,8 @@ export class NameScopedTokenSet {
return Array.from(this.methodScopedTokenSetByMethodKey.entries());
}

public keys() {
return this.methodScopedTokenSetByMethodKey.keys();
public keys(): string[] {
return Array.from(this.methodScopedTokenSetByMethodKey.keys());
}

public clear(): void {
Expand Down Expand Up @@ -1488,19 +1586,27 @@ export class NameScopedTokenSet {
export class RememberedToken {
_type: string;
_modifiers: string[] = [];
constructor(type: string, modifiers: string[] | undefined) {
_lineIdx: number;
constructor(type: string, lineIdx: number, modifiers: string[] | undefined) {
this._type = type;
this._lineIdx = lineIdx;
if (modifiers != undefined) {
this._modifiers = modifiers;
}
}

get type(): string {
return this._type;
}

get modifiers(): string[] {
return this._modifiers;
}

get lineIndex(): number {
return this._lineIdx;
}

public isPublic(): boolean {
// is symbol from CON section or is PUB method?
let publicStatus: boolean = false;
Expand Down
Loading

0 comments on commit 3354eee

Please sign in to comment.