diff --git a/spin2/CHANGELOG.md b/spin2/CHANGELOG.md index 45cd69a..91d94c9 100644 --- a/spin2/CHANGELOG.md +++ b/spin2/CHANGELOG.md @@ -22,6 +22,16 @@ Possible next additions: - Add additional Snippets as the community identifies them +## [1.8.0] 2022-12-23 + +Add [optional] FlexSpin preprocessor support (P1 & P2), Repair semantic highlight (P2) + +- Add new extension setting to enable recognition of FlexSpin Preprocessor Directives (Default: disabled) +- Adds flagging of Preprocessor directive lines as unknown when FlexSpin support is not enabled +- Fix P2 recognition of _RET_ directive in Pasm2 +- Fix P2 recognition of built-in _set, _clr variables in Pasm2 +- Fix P1 & P2 recognition of constants when assignment uses #> and <# operators + ## [1.7.8] 2022-12-22 Minor tabbing update diff --git a/spin2/TEST/spin2/cinepak.spin2 b/spin2/TEST/spin2/cinepak.spin2 new file mode 100644 index 0000000..66862d6 --- /dev/null +++ b/spin2/TEST/spin2/cinepak.spin2 @@ -0,0 +1,665 @@ + +CON + +MAX_STRIPS = 4 + +MAX_WIDTH = 640 +MAX_HEIGHT = 480 + +INBUFFER_SIZE = 2*1024 + +'#define MODE_24BPP +#define MODE_16BPP +'#define MODE_8BPP + +#define _PROFILE_ + +CON + +#ifdef MODE_24BPP +PIXEL_SIZE = 4 +#elseifdef MODE_16BPP +PIXEL_SIZE = 2 +#elseifdef MODE_8BPP +PIXEL_SIZE = 1 +#endif + +VECTOR_SIZE = 2*2*PIXEL_SIZE + +CODEBOOK_SIZE = 256*VECTOR_SIZE + +STRIP_STATE_SIZE = 2*CODEBOOK_SIZE + +ID_STRIP_INTRA = $10 +ID_STRIP_INTER = $11 + +ID_V4_COLOR_FULL = $20 +ID_V4_COLOR_PARTIAL = $21 +ID_V1_COLOR_FULL = $22 +ID_V1_COLOR_PARTIAL = $23 +ID_V4_MONO_FULL = $24 +ID_V4_MONO_PARTIAL = $25 +ID_V1_MONO_FULL = $26 +ID_V1_MONO_PARTIAL = $27 + +PARTIAL_BIT = 0 +V1SEL_BIT = 1 +MONO_BIT = 2 + +ID_IMAGE_INTRA = $30 +ID_IMAGE_INTER = $31 +ID_IMAGE_V1 = $32 + +MAX_CODEBOOK = 256*6 + 256/8 +MAX_BLOCKROW = (MAX_WIDTH/4)*4 + ((MAX_WIDTH/4)/4) + 8 + +HEADBUFFER_SIZE = (((MAX_CODEBOOK #> (MAX_BLOCKROW*2) #> INBUFFER_SIZE) + 63)/64)*64 ' size must be multiple of 64 for block wrapping reasons + +#0, name1, name2, name23 ' smm added this as test + +VAR + +long infunc,rdfunc,wrfunc,syncfunc,cbfunc +long curframe,prevframe +byte bufferflag,prev_strips,_pad[2] + +#ifdef _PROFILE_ +long vq_cycles,cb_cycles,io_cycles,pftime +#endif + +byte headbuffer[HEADBUFFER_SIZE] ' more than enough to replenish an entire codebook + +long blockbuffer1[MAX_WIDTH*PIXEL_SIZE] +long blockbuffer2[MAX_WIDTH*PIXEL_SIZE] + +byte codebook_buffer[MAX_STRIPS*STRIP_STATE_SIZE] + + +PUB init(input_func,memory_rd_func,memory_wr_func,memory_sync_func,callback_func) + + infunc := input_func + rdfunc := memory_rd_func + wrfunc := memory_wr_func + syncfunc := memory_sync_func + cbfunc := callback_func + +PUB decode_frame(destination,prev_frame) | frameleft,stripcount,stripno,width,height,buffer,x1,x2,word y1,word y2,tmp,frameflags + + prevframe := prev_frame == -1 ? curframe : prev_frame + curframe := destination + +#ifdef _PROFILE_ + vq_cycles := cb_cycles := io_cycles := 0 +#endif + + ' read frame header + infunc(@headbuffer,10) + + frameflags := headbuffer.byte[0] + + frameleft := (__builtin_bswap32(headbuffer.long[0]) zerox 23) - 10 + debug(sdec(frameleft)) + + if frameleft <= 12 + abort @"bad header" + + width := __builtin_bswap16(headbuffer.word[2]) + if width > MAX_WIDTH + abort @"too wide" + height := __builtin_bswap16(headbuffer.word[3]) + if height > MAX_HEIGHT + abort @"too tall" + + stripcount := __builtin_bswap16(headbuffer.word[4]) + if stripcount > MAX_STRIPS + abort @"too many strips" + + if stripcount == 0 + '' TODO: Copy frame to new buffer + '' TODO TODO TODO remove this shit and copy all space between strips + if prevframe <> curframe + tmp := 0 + repeat while height > 0 + rdfunc(@blockbuffer1,prevframe+tmp,MAX_WIDTH*PIXEL_SIZE*8,true) + wrfunc(@blockbuffer1,curframe+tmp,MAX_WIDTH*PIXEL_SIZE*8,true) + height -= 8 + tmp += MAX_WIDTH*PIXEL_SIZE*8 + syncfunc() + return false + + stripno := 0 + y2 := 0 + repeat + + debug(udec(stripno,stripcount)) + + if frameflags.[0] + ' need to copy previous strip's codebook + tmp := stripno ? stripno-1 : prev_strips + longmove(@codebook_buffer+stripno*STRIP_STATE_SIZE, @codebook_buffer+tmp*STRIP_STATE_SIZE, STRIP_STATE_SIZE/4) + + ' read strip header + infunc(@headbuffer,12) + + ' + y1 := __builtin_bswap16(headbuffer.word[2]) + tmp := __builtin_bswap16(headbuffer.word[4]) + if y1 == 0 ' weird special case + y1 := y2 + y2 += tmp + else + y2 := tmp + x1 := __builtin_bswap16(headbuffer.word[3]) + x2 := __builtin_bswap16(headbuffer.word[5]) + + ifnot x1 < x2 && y1 < y2 && x2 <= MAX_WIDTH && y2 <= MAX_HEIGHT + debug(sdec(x1,x2,y1,y2)) + abort @"bad strip position" + + ' get data chunks + repeat + infunc(@headbuffer,4) + tmp := headbuffer.long[0] + case tmp & 255 + ID_V4_COLOR_FULL..ID_V1_MONO_PARTIAL: + decode_codebook(stripno,tmp&255,__builtin_bswap32(tmp&!255)-4) + ID_IMAGE_INTRA..ID_IMAGE_V1: + decode_vectors(stripno,x1,y1,x2-x1,y2-y1,tmp&255,__builtin_bswap32(tmp&!255)-4) + quit + + + + + while ++stripno < stripcount + prev_strips := stripcount + +PRI decode_vectors(stripno,x_offset,y_offset,width,height,chunk_type,size) | datptr, fillptr, blkwidth, blkheight, bits, bit_left, tmp, tmp2, temp, cbbase, startptr, endptr, iptr, blkleft, datptr_old, input_fill, buffer, pixels[12], word realrows, word realrows_next + +#ifdef _PROFILE_ + pftime := getct() +#endif + + blkwidth := (width+3)>>2 + 'blkheight := (width+3)>>2 + + if chunk_type == ID_IMAGE_V1 + bits := 0 + bit_left := posx + else + bit_left := 0 + + startptr := @headbuffer + datptr := @headbuffer + fillptr := @headbuffer + endptr := @headbuffer + HEADBUFFER_SIZE + + debug(uhex_long(startptr,endptr)) + + input_fill := 0 + + cbbase := STRIP_STATE_SIZE*stripno + @codebook_buffer + + debug("Decoding vectors ",uhex_byte(chunk_type),sdec(stripno,x_offset,y_offset,width,height,size)) + + if chunk_type == ID_IMAGE_INTER + ' Fetch previous frame data for first line + realrows := height <# 4 +#ifdef _PROFILE_ + tmp := getct() +#endif + rdfunc(bufferflag ? @blockbuffer2 : @blockbuffer1,prevframe+(y_offset zerox 15)*MAX_WIDTH*PIXEL_SIZE,realrows*MAX_WIDTH*PIXEL_SIZE,true) +#ifdef _PROFILE_ + tmp := getct()-tmp + pftime += tmp + io_cycles += tmp +#endif + + repeat + blkleft := blkwidth + realrows := height <# 4 + realrows_next := (height-4) <# 4 + buffer := (bufferflag ? @blockbuffer2 : @blockbuffer1) + iptr := buffer + x_offset*PIXEL_SIZE + not bufferflag + if chunk_type == ID_IMAGE_INTER && height > 4 + ' Fetch previous frame data for next line asynchronusly +#ifdef _PROFILE_ + tmp := getct() +#endif + rdfunc(bufferflag ? @blockbuffer2 : @blockbuffer1,prevframe+((y_offset+4) zerox 15)*MAX_WIDTH*PIXEL_SIZE,realrows_next*MAX_WIDTH*PIXEL_SIZE,true) +#ifdef _PROFILE_ + tmp := getct()-tmp + pftime += tmp + io_cycles+= tmp +#endif + + + ifnot size or input_fill + debug(uhex_long(datptr,fillptr,endptr),sdec(size,input_fill)) + abort @"vector chunk too short" + + repeat while size && input_fill < MAX_BLOCKROW + ' fill to end or to ring buffer boundary + tmp := ((datptr <= fillptr ? endptr : datptr) - fillptr) <# size + debug(sdec(size,input_fill,tmp,height),uhex_long(startptr,datptr,fillptr,endptr)) + 'debug("Buffer refill") +#ifdef _PROFILE_ + tmp2 := getct() +#endif + infunc(fillptr,tmp) +#ifdef _PROFILE_ + tmp2 := getct()-tmp2 + pftime += tmp2 +#endif + 'debug(uhex_byte_array(fillptr,32)) + fillptr += tmp + if fillptr == endptr + fillptr := startptr + input_fill += tmp + size -= tmp + + datptr_old := datptr + + 'debug("Decoding 4 rows at ",sdec(x_offset,y_offset,blkleft),uhex_long(datptr,iptr)) + + org + ' I hate everything (re-align FIFO) + mov tmp,datptr + subr tmp,endptr + mov tmp2,tmp + subr tmp2,#64 + and tmp2,#63 + sub datptr,tmp2 + rczr tmp2 wcz + mov tmp,datptr + subr tmp,endptr + shr tmp,#6 + sub tmp,#0 ' DEBUG + rdfast tmp,datptr + fblock .fifo_blocks,startptr + if_z rfbyte tmp + if_c rfword tmp + and tmp2,#15 wz + if_nz rep #1,tmp2 + if_nz rflong tmp + ' Self-modify away skip check for intra-coded modes + cmp chunk_type,#ID_IMAGE_INTER wz + if_nz add .next_block,#4 + if_nz jmp $+5 + ' This also means that Z flag is now set in inter coded mode + +.blkloop + 'debug("Block loop ",udec(blkleft),uhex_long(iptr)) +.skipcheck sub bit_left,#1 wc + if_c call #.replenish_bits +.skipcheck2 rol bits,#1 wc + if_nc jmp #.skip_block + + ' V4 if bit set + sub bit_left,#1 wc + if_c call #.replenish_bits + rol bits,#1 wc + if_nc jmp #.v1 + + rep @.v4loop,#2 +#ifdef MODE_24BPP + rfbyte tmp + mul tmp,#VECTOR_SIZE + add tmp,cbbase + setq #3 + rdlong pixels+0,tmp + rfbyte tmp + mul tmp,#VECTOR_SIZE + add tmp,cbbase + setq #3 + rdlong pixels+8,tmp + ' 0 1 4 5 x x x x 2 3 6 7 + + mov pixels+4,pixels+2 + mov pixels+5,pixels+3 + mov pixels+2,pixels+8 + mov pixels+3,pixels+9 + mov pixels+8,pixels+4 + mov pixels+9,pixels+5 + ' 0 1 2 3 x x x x 4 5 6 7 + + setq#3 + wrlong pixels+0,iptr + add iptr,.stride + setq#3 + wrlong pixels+8,iptr + add iptr,.stride +#elseifdef MODE_16BPP + rfbyte tmp + mul tmp,#VECTOR_SIZE + add tmp,cbbase + setq #1 + rdlong pixels+0,tmp + rfbyte tmp + mul tmp,#VECTOR_SIZE + add tmp,cbbase + setq #1 + rdlong pixels+4,tmp + ' 01 45 xx xx 23 67 + + mov pixels+2,pixels+1 + mov pixels+1,pixels+4 + mov pixels+4,pixels+2 + + ' 01 23 xx xx 45 67 + + setq#1 + wrlong pixels+0,iptr + add iptr,.stride + setq#1 + wrlong pixels+4,iptr + add iptr,.stride + +#elseifdef MODE_8BPP + + rfbyte tmp + mul tmp,#VECTOR_SIZE + add tmp,cbbase + rdlong pixels+0,tmp + rfbyte tmp + mul tmp,#VECTOR_SIZE + add tmp,cbbase + rdlong pixels+2,tmp + ' 0145 xxxx 2367 + + getword pixels+1,pixels+0,#1 + setword pixels+0,pixels+2,#1 + setword pixels+2,pixels+1,#0 + ' 0123 xxxx 4567 + + wrlong pixels+0,iptr + add iptr,.stride + wrlong pixels+2,iptr + add iptr,.stride +#endif +.v4loop + sub iptr,.stride_nextblock wz ' clear Z flag + jmp #.next_block +.v1 + rfbyte tmp + add tmp,#256 + mul tmp,#VECTOR_SIZE + add tmp,cbbase +#ifdef MODE_24BPP + setq #3 + rdlong pixels+0,tmp + ' he thicc + ' 0 1 2 3 x x x x + mov pixels+7,pixels+3 + mov pixels+6,pixels+3 + mov pixels+5,pixels+2 + mov pixels+4,pixels+2 + ' 0 1 2 3 2 2 3 3 + mov pixels+3,pixels+1 + mov pixels+2,pixels+1 + mov pixels+1,pixels+0 + ' 0 0 1 1 2 2 3 3 + + setq#3 + wrlong pixels+0,iptr + add iptr,.stride + setq#3 + wrlong pixels+0,iptr + add iptr,.stride + setq#3 + wrlong pixels+4,iptr + add iptr,.stride + setq#3 + wrlong pixels+4,iptr +#elseifdef MODE_16BPP + setq #1 + rdlong pixels+0,tmp + ' he thicc + ' 01 23 xx xx + mov pixels+2,pixels+1 + mov pixels+1,pixels+0 + mov pixels+3,pixels+1 + ' 01 01 23 23 + movbyts pixels+0,#%%1010 + movbyts pixels+1,#%%3232 + movbyts pixels+2,#%%1010 + movbyts pixels+3,#%%3232 + ' 00 11 22 33 + setq#1 + wrlong pixels+0,iptr + add iptr,.stride + setq#1 + wrlong pixels+0,iptr + add iptr,.stride + setq#1 + wrlong pixels+2,iptr + add iptr,.stride + setq#1 + wrlong pixels+2,iptr +#elseifdef MODE_8BPP + rdlong pixels+0,tmp + ' he thicc + ' 0123 xxxx + mov pixels+1,pixels+0 + ' 0123 0123 + movbyts pixels+0,#%%1100 + movbyts pixels+1,#%%3322 + ' 0011 2233 + wrlong pixels+0,iptr + add iptr,.stride + wrlong pixels+0,iptr + add iptr,.stride + wrlong pixels+1,iptr + add iptr,.stride + wrlong pixels+1,iptr +#endif + sub iptr,.stride3 wz ' clear Z flag +.skip_block + add iptr,#4*PIXEL_SIZE ' advance horizontal +.next_block + djnz blkleft,#.blkloop + wrz tmp + _ret_ getptr datptr + + +.replenish_bits + rflong bits + movbyts bits,#%%0123 + 'pop tmp ' DEBUG DEBUG + 'getptr datptr + _ret_ add bit_left,#32 + + ' TODO: Remove this workaround for flexspin bug + mov pixels+0,pixels+1 + mov pixels+2,pixels+3 + mov pixels+4,pixels+5 + mov pixels+6,pixels+7 + mov pixels+8,pixels+9 + mov pixels+10,pixels+11 + + +.selfmod_clrc modc _clr wc +.selfmod_setc modc _set wc +.stride long MAX_WIDTH*PIXEL_SIZE +.stride3 long MAX_WIDTH*PIXEL_SIZE*3 +.stride_nextblock long MAX_WIDTH*PIXEL_SIZE*4 - 4*PIXEL_SIZE +.fifo_blocks long HEADBUFFER_SIZE/64 + end + + ' ??? + if datptr >= endptr + datptr -= HEADBUFFER_SIZE + + 'debug(uhex_long(bits,datptr,tmp)) + + 'debug("Doneski ",uhex_long(datptr,iptr),sdec(x_offset)) + + ' if decoding in-place, skip writing unchanged lines + ifnot chunk_type == ID_IMAGE_INTER && tmp && prevframe == curframe +#ifdef _PROFILE_ + tmp := getct() +#endif + wrfunc(curframe+(y_offset zerox 15)*MAX_WIDTH*PIXEL_SIZE,buffer,realrows*MAX_WIDTH*PIXEL_SIZE,true) +#ifdef _PROFILE_ + tmp := getct()-tmp + pftime += tmp + io_cycles += tmp +#endif + + tmp := datptr - datptr_old + if tmp < 0 + tmp += HEADBUFFER_SIZE + input_fill -= tmp + if input_fill < 0 + debug(sdec(input_fill,tmp,bit_left),uhex_long(datptr,datptr_old,startptr,endptr)) + abort @"weird buffer error" + + height -= 4 + y_offset += 4 + while height > 0 + + debug("Too much data? ",sdec(size)) + repeat while size + abort @"data beyond end of strip" + tmp := size <# HEADBUFFER_SIZE + infunc(@headbuffer,tmp) + size -= tmp + +#ifdef _PROFILE_ + vq_cycles += getct() - pftime +#endif + +PRI decode_codebook(stripno,chunk_type,size) | cbptr, datptr, bit_left, bits, pixels[4], i, u, v, tmp, mask, val, datend + + if size >+ HEADBUFFER_SIZE + debug(sdec(size),sdec(HEADBUFFER_SIZE)) + abort @"Codebook chunk too large" + + debug("Decoding codebook ",uhex_byte(chunk_type)) + infunc(@headbuffer,size) + + ' start profile here so we don't double count FS time +#ifdef _PROFILE_ + pftime := getct() +#endif + + cbptr := STRIP_STATE_SIZE*stripno + @codebook_buffer + if chunk_type.[V1SEL_BIT] + cbptr += CODEBOOK_SIZE + + if chunk_type.[PARTIAL_BIT] + bit_left := 1 + i := 256/32 + else + bit_left := size +/ (chunk_type.[MONO_BIT]?4:6) + 1 + i := 1 + bits := -1 + + datptr := @headbuffer + datend := datptr + size + debug(uhex_long(cbptr)) + ' Hardcore part + org + rdfast #0,datptr + testb chunk_type,#MONO_BIT wc + if_c mov .get_u,.selfmod_nocolor + jmp #.loop_entry + +.loop_top + rol bits,#1 wc + if_nc jmp #.loop_bottom + + rflong pixels+0 ' get Y*4 +#ifndef MODE_8BPP + getbyte pixels+1,pixels+0,#1 + getbyte pixels+2,pixels+0,#2 + getbyte pixels+3,pixels+0,#3 + movbyts pixels+0,#%%0000 + movbyts pixels+1,#%%0000 + movbyts pixels+2,#%%0000 + movbyts pixels+3,#%%0000 + +.get_u rfbyte u wc + bitc mask,#8 addbits 7 ' blue + if_c subr u,#256 + mov tmp,u + shr tmp,#1 + negnc tmp + shl u,#8 + rfbyte v wc + bitc mask,#24 addbits 7 ' red + if_c subr v,#256 + sumc tmp,v + setbyte u,v,#3 + addpix u,u ' double magnitude + + abs tmp wc + fle tmp,#255 + setbyte u,tmp,#2 + bitnc mask,#16 addbits 7 ' green + + xor pixels+0,mask + xor pixels+1,mask + xor pixels+2,mask + xor pixels+3,mask + addpix pixels+0,u + addpix pixels+1,u + addpix pixels+2,u + addpix pixels+3,u + xor pixels+0,mask + xor pixels+1,mask + xor pixels+2,mask + xor pixels+3,mask +.no_color +#ifndef MODE_16BPP + ' 24bpp + setq #3 + wrlong pixels+0,cbptr +#else + ' 16bpp + rgbsqz pixels+0 + rgbsqz pixels+1 + rgbsqz pixels+2 + rgbsqz pixels+3 + setword pixels+0,pixels+1,#1 + setword pixels+1,pixels+2,#0 + setword pixels+1,pixels+3,#1 + setq #1 + wrlong pixels+0,cbptr +#endif + +#else + ' 8bpp +.get_u rfword u + wrlong pixels+0,cbptr +#endif + +.loop_bottom + add cbptr,#VECTOR_SIZE +.loop_entry + djnz bit_left,#.loop_top +.replenish_bits + rflong bits + movbyts bits,#%%0123 + mov bit_left,#32 + getptr datptr + sub datptr,datend wc + _ret_ tjs datptr,#.loop_top + +#ifndef MODE_8BPP +.selfmod_nocolor jmp #\.no_color +#else +.selfmod_nocolor nop +#endif + + end + debug(uhex_long(cbptr)) +#ifdef _PROFILE_ + cb_cycles += getct() - pftime +#endif + + +#ifdef _PROFILE_ +PUB get_profile() : vq,cb,io + return vq_cycles,cb_cycles,io_cycles +#endif diff --git a/spin2/package.json b/spin2/package.json index 28c13f6..5f5edee 100644 --- a/spin2/package.json +++ b/spin2/package.json @@ -3,7 +3,7 @@ "displayName": "Spin2", "description": "Spin2/Pasm2 Syntax/Semantic Highlighting w/Code Outline and Custom tabbing support", "icon": "images/Propeller.ico", - "version": "1.7.8", + "version": "1.8.0", "publisher": "IronSheepProductionsLLC", "repository": { "type": "git", @@ -160,6 +160,16 @@ } ], "configuration": [ + { + "title": "Spin2 Extension Behaviors", + "properties": { + "spinExtensionBehavior.highlightFlexspinDirectives": { + "type": "boolean", + "default": false, + "description": "Enable recognition/validation of flexspin preprocessor directives." + } + } + }, { "title": "Spin2 Tabstops", "properties": { diff --git a/spin2/src/spin1.semanticAndOutline.ts b/spin2/src/spin1.semanticAndOutline.ts index f6498b1..ac0d6ad 100644 --- a/spin2/src/spin1.semanticAndOutline.ts +++ b/spin2/src/spin1.semanticAndOutline.ts @@ -1,9 +1,9 @@ "use strict"; - // src/spin1.semanticAndOutline.ts import * as vscode from "vscode"; -import { Z_UNKNOWN } from "zlib"; +//import { Z_UNKNOWN } from "zlib"; +import { semanticConfiguration, reloadSemanticConfiguration } from "./spin2.semantic.configuration"; // ---------------------------------------------------------------------------- // this file contains both an outline provider @@ -201,9 +201,11 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman // SEE https://www.codota.com/code/javascript/functions/vscode/CancellationToken/isCancellationRequested if (cancelToken) { } // silence our compiler for now... TODO: we should adjust loop so it can break on cancelToken.isCancellationRequested + this._resetForNewDocument(); + this._logMessage("* Config: spinExtensionBehavior.highlightFlexspinDirectives: [" + this.configuration.highlightFlexspin + "]"); + const allTokens = this._parseText(document.getText()); const builder = new vscode.SemanticTokensBuilder(); - this._resetForNewDocument(); allTokens.forEach((token) => { builder.push(token.line, token.startCharacter, token.length, this._encodeTokenType(token.ptTokenType), this._encodeTokenModifiers(token.ptTokenModifiers)); }); @@ -215,6 +217,8 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman private localPasmTokensByMethodName = new Map>(); private conEnumInProgress: boolean = false; + private configuration = semanticConfiguration; + // list of directives found in file private currentMethodName: string = ""; @@ -236,6 +240,9 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman this.localPasmTokensByMethodName.clear(); this.conEnumInProgress = false; this.currentMethodName = ""; + if (reloadSemanticConfiguration()) { + this.configuration = semanticConfiguration; + } } private _encodeTokenModifiers(strTokenModifiers: string[]): number { @@ -281,6 +288,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman const trimmedLine = line.trim(); const trimmedNonCommentLine = this._getNonCommentLineRemainder(0, line); const sectionStatus = this._isSectionStartLine(line); + const lineParts: string[] = trimmedNonCommentLine.split(/[ \t]/); if (currState == eParseState.inMultiLineComment) { // in multi-line non-doc-comment, hunt for end '}' to exit let closingOffset = line.indexOf("}"); @@ -299,6 +307,9 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman } // DO NOTHING Let Syntax highlighting do this continue; + } else if (this._isFlexspinPreprocessorDirective(lineParts[0])) { + this._getPreProcessor_Declaration(0, i + 1, line); + continue; } else if (sectionStatus.isSectionStart) { currState = sectionStatus.inProgressStatus; this._logState("- scan ln:" + (i + 1) + " currState=[" + currState + "]"); @@ -433,6 +444,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman const line = lines[i]; const trimmedLine = line.trim(); const sectionStatus = this._isSectionStartLine(line); + const lineParts: string[] = trimmedLine.split(/[ \t]/); // TODO: UNDONE add filter which corrects for syntax inability to mark 'comments when more than one "'" present on line! //if (trimmedLine.length > 2 && trimmedLine.includes("'")) { // const partialTokenSet: IParsedToken[] = this._possiblyMarkBrokenSingleLineComment(i, 0, line); @@ -456,6 +468,13 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman currState = priorState; } // DO NOTHING Let Syntax highlighting do this + } else if (this._isFlexspinPreprocessorDirective(lineParts[0])) { + const partialTokenSet: IParsedToken[] = this._reportPreProcessorLine(i, 0, line); + partialTokenSet.forEach((newToken) => { + this._logPreProc("=> PreProc: " + this._tokenString(newToken, line)); + tokenSet.push(newToken); + }); + continue; } else if (sectionStatus.isSectionStart) { currState = sectionStatus.inProgressStatus; this._logState(" -- ln:" + (i + 1) + " currState=[" + currState + "]"); @@ -477,7 +496,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman if (line.length > 3) { const partialTokenSet: IParsedToken[] = this._reportCON_DeclarationLine(i, 3, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> CON: " + this._tokenString(newToken, line)); + this._logCON("=> CON: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -580,7 +599,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman this._logCON("- process CON line(" + (i + 1) + "): trimmedLine=[" + trimmedLine + "]"); const partialTokenSet: IParsedToken[] = this._reportCON_DeclarationLine(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> CON: " + this._tokenString(newToken, line)); + this._logCON("=> CON: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -618,7 +637,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman } else { const partialTokenSet: IParsedToken[] = this._reportDAT_DeclarationLine(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> DAT: " + this._tokenString(newToken, line)); + this._logDAT("=> DAT: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -629,7 +648,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman this._logVAR("- process VAR line(" + (i + 1) + "): trimmedLine=[" + trimmedLine + "]"); const partialTokenSet: IParsedToken[] = this._reportVAR_DeclarationLine(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> VAR: " + this._tokenString(newToken, line)); + this._logVAR("=> VAR: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -650,7 +669,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman // in DAT sections we end with FIT or just next section const partialTokenSet: IParsedToken[] = this._reportDAT_PasmCode(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> DAT: " + this._tokenString(newToken, line)); + this._logPASM("=> DAT: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); const lineParts: string[] = trimmedLine.split(/[ \t]/); @@ -677,6 +696,25 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman return tokenSet; } + private _getPreProcessor_Declaration(startingOffset: number, lineNbr: number, line: string): void { + if (this.configuration.highlightFlexspin) { + let currentOffset: number = this._skipWhite(line, startingOffset); + const nonCommentConstantLine = this._getNonCommentLineRemainder(currentOffset, line); + // get line parts - we only care about first one + const lineParts: string[] = nonCommentConstantLine.split(/[ \t=]/); + this._logPreProc(" - ln:" + lineNbr + " GetPreProcDecl lineParts=[" + lineParts + "]"); + const directive: string = lineParts[0]; + const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; + if (this._isFlexspinPreprocessorDirective(directive)) { + // check a valid preprocessor line for a declaration + if (symbolName != undefined && directive.toLowerCase() == "#define") { + this._logPreProc(" -- new PreProc Symbol=[" + symbolName + "]"); + this._setGlobalToken(symbolName, new RememberedToken("variable", ["readonly"])); + } + } + } + } + private _getCON_Declaration(startingOffset: number, lineNbr: number, line: string): void { // HAVE DIGIT_NO_VALUE = -2 ' digit value when NOT [0-9] // -or- _clkmode = xtal1 + pll16x @@ -687,7 +725,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman const nonCommentConstantLine = this._getNonCommentLineRemainder(currentOffset, line); this._logCON(" - ln:" + lineNbr + " GetCONDecl nonCommentConstantLine=[" + nonCommentConstantLine + "]"); - const haveEnumDeclaration: boolean = nonCommentConstantLine.indexOf("#") != -1; + const haveEnumDeclaration: boolean = nonCommentConstantLine.startsWith("#"); const containsMultiAssignments: boolean = nonCommentConstantLine.indexOf(",") != -1; let statements: string[] = [nonCommentConstantLine]; if (!haveEnumDeclaration && containsMultiAssignments) { @@ -919,13 +957,94 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman } } + private _reportPreProcessorLine(lineNumber: number, startingOffset: number, line: string): IParsedToken[] { + const tokenSet: IParsedToken[] = []; + // skip Past Whitespace + let currentOffset: number = this._skipWhite(line, startingOffset); + const nonCommentConstantLine = this._getNonCommentLineRemainder(currentOffset, line); + // get line parts - we only care about first one + const lineParts: string[] = nonCommentConstantLine.split(/[ \t=]/); + this._logPreProc(" - ln:" + lineNumber + " reportPreProc lineParts=[" + lineParts + "]"); + const directive: string = lineParts[0]; + const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; + if (this.configuration.highlightFlexspin) { + if (this._isFlexspinPreprocessorDirective(directive)) { + // record the directive + tokenSet.push({ + line: lineNumber, + startCharacter: 0, + length: directive.length, + ptTokenType: "keyword", + ptTokenModifiers: ["control", "directive"], + }); + const hasSymbol: boolean = + directive.toLowerCase() == "#define" || + directive.toLowerCase() == "#ifdef" || + directive.toLowerCase() == "#ifndef" || + directive.toLowerCase() == "#elseifdef" || + directive.toLowerCase() == "#elseifndef"; + if (hasSymbol && symbolName != undefined) { + const nameOffset = line.indexOf(symbolName, currentOffset); + this._logPreProc(" -- GLBL symbolName=[" + symbolName + "]"); + let referenceDetails: RememberedToken | undefined = undefined; + if (this._isGlobalToken(symbolName)) { + referenceDetails = this._getGlobalToken(symbolName); + this._logPreProc(" -- FOUND preProc global " + this._rememberdTokenString(symbolName, referenceDetails)); + } + if (referenceDetails != undefined) { + // record a constant declaration! + const updatedModificationSet: string[] = directive.toLowerCase() == "#define" ? this._modifiersWith(referenceDetails.modifiers, "declaration") : referenceDetails.modifiers; + tokenSet.push({ + line: lineNumber, + startCharacter: nameOffset, + length: symbolName.length, + ptTokenType: referenceDetails.type, + ptTokenModifiers: updatedModificationSet, + }); + } else if (this._isFlexspinReservedWord(symbolName)) { + // record a constant reference + tokenSet.push({ + line: lineNumber, + startCharacter: nameOffset, + length: symbolName.length, + ptTokenType: "variable", + ptTokenModifiers: ["readonly"], + }); + } else { + // record an unknown name + tokenSet.push({ + line: lineNumber, + startCharacter: nameOffset, + length: symbolName.length, + //ptTokenType: "variable", + //ptTokenModifiers: ["readonly", "missingDeclaration"], + ptTokenType: "comment", + ptTokenModifiers: ["line"], + }); + } + } + } + } else { + // DO NOTHING we don't highlight these (flexspin support not enabled) + tokenSet.push({ + line: lineNumber, + startCharacter: 0, + length: lineParts[0].length, + ptTokenType: "macro", + ptTokenModifiers: ["directive", "illegalUse"], + }); + } + + return tokenSet; + } + private _reportCON_DeclarationLine(lineNumber: number, startingOffset: number, line: string): IParsedToken[] { const tokenSet: IParsedToken[] = []; // skip Past Whitespace let currentOffset: number = this._skipWhite(line, startingOffset); const nonCommentConstantLine = this._getNonCommentLineReturnComment(lineNumber, currentOffset, line, tokenSet); - const haveEnumDeclaration: boolean = nonCommentConstantLine.indexOf("#") != -1; + const haveEnumDeclaration: boolean = nonCommentConstantLine.startsWith("#"); const containsMultiAssignments: boolean = nonCommentConstantLine.indexOf(",") != -1; this._logCON("- reportConstant haveEnum=(" + haveEnumDeclaration + "), containsMulti=(" + containsMultiAssignments + "), nonCommentConstantLine=[" + nonCommentConstantLine + "]"); let statements: string[] = [nonCommentConstantLine]; @@ -2579,6 +2698,7 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman // adjust following true/false to show specific parsing debug private spin1DebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showSpinCode: boolean = true; + private showPreProc: boolean = true; private showCON: boolean = true; private showOBJ: boolean = true; private showDAT: boolean = true; @@ -2606,6 +2726,12 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman } } + private _logPreProc(message: string): void { + if (this.showPreProc) { + this._logMessage(message); + } + } + private _logCON(message: string): void { if (this.showCON) { this._logMessage(message); @@ -3723,6 +3849,38 @@ export class Spin1DocumentSemanticTokensProvider implements vscode.DocumentSeman return reservedStatus; } + private _isFlexspinPreprocessorDirective(name: string): boolean { + const flexspinDirectiveOfNote: string[] = ["#define", "#ifdef", "#ifndef", "#else", "#elseifdef", "#elseifndef", "#endif", "#error", "#include", "#warn", "#undef"]; + const reservedStatus: boolean = flexspinDirectiveOfNote.indexOf(name.toLowerCase()) != -1; + return reservedStatus; + } + + private _isFlexspinReservedWord(name: string): boolean { + const flexspinReservedswordsOfNote: string[] = [ + "__propeller__", + "__propeller2__", + "__p2__", + "__flexspin__", + "__spincvt__", + "__spin2pasm__", + "__spin2cpp__", + "__have_fcache__", + "__cplusplus__", + "__date__", + "__file__", + "__line__", + "__time__", + "__version__", + "__debug__", + "__output_asm__", + "__output_bytecode__", + "__output_c__", + "__output_cpp__", + ]; + const reservedStatus: boolean = flexspinReservedswordsOfNote.indexOf(name.toLowerCase()) != -1; + return reservedStatus; + } + private _isReservedPasmSymbols(name: string): boolean { const reservedPasmSymbolNames: string[] = ["org", "fit", "end"]; const reservedStatus: boolean = reservedPasmSymbolNames.indexOf(name.toLowerCase()) != -1; diff --git a/spin2/src/spin2.extension.ts b/spin2/src/spin2.extension.ts index 3569533..eaa71fd 100644 --- a/spin2/src/spin2.extension.ts +++ b/spin2/src/spin2.extension.ts @@ -1,6 +1,6 @@ "use strict"; -import { toNamespacedPath } from "path"; +//import { toNamespacedPath } from "path"; //import { stringify } from 'querystring'; //import { createStringLiteralFromNode, EndOfLineState } from 'typescript'; // src/spin2.extension.ts @@ -8,8 +8,6 @@ import { toNamespacedPath } from "path"; import * as vscode from "vscode"; import { Formatter } from "./spin.tabFormatter"; -import { tabConfiguration, reloadTabConfiguration } from "./spin.tabFormatter.configuration"; - import { overtypeBeforePaste, overtypeBeforeType } from "./spin.editMode.behavior"; import { configuration, reloadConfiguration } from "./spin.editMode.configuration"; import { getMode, resetModes, toggleMode, toggleMode2State, eEditMode, modeName } from "./spin.editMode.mode"; diff --git a/spin2/src/spin2.semantic.configuration.ts b/spin2/src/spin2.semantic.configuration.ts new file mode 100644 index 0000000..1c51c89 --- /dev/null +++ b/spin2/src/spin2.semantic.configuration.ts @@ -0,0 +1,27 @@ +"use strict"; +// src/spin2.semantic.configuration.ts + +import * as vscode from "vscode"; + +const loadSemanticConfiguration = () => { + const semanticConfiguration = vscode.workspace.getConfiguration("spinExtensionBehavior"); + + return { + highlightFlexspin: semanticConfiguration.get("highlightFlexspinDirectives"), + }; +}; + +export const semanticConfiguration = loadSemanticConfiguration(); + +export const reloadSemanticConfiguration = () => { + const newSemanticConfiguration = loadSemanticConfiguration(); + + // bail out if nothing changed + if (semanticConfiguration.highlightFlexspin === newSemanticConfiguration.highlightFlexspin) { + return false; + } + + semanticConfiguration.highlightFlexspin = newSemanticConfiguration.highlightFlexspin; + + return true; +}; diff --git a/spin2/src/spin2.semanticAndOutline.ts b/spin2/src/spin2.semanticAndOutline.ts index 622b0fd..b8a45d4 100644 --- a/spin2/src/spin2.semanticAndOutline.ts +++ b/spin2/src/spin2.semanticAndOutline.ts @@ -1,11 +1,13 @@ "use strict"; - -import { deepStrictEqual } from "assert"; // src/spin2.semanticAndOutline.ts +//import { deepStrictEqual } from "assert"; + +import { semanticConfiguration, reloadSemanticConfiguration } from "./spin2.semantic.configuration"; + import * as vscode from "vscode"; -// ---------------------------------------------------------------------------- +// ============================================================================ // this file contains both an outline provider // and our semantic highlighting provider // @@ -238,9 +240,11 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman // SEE https://www.codota.com/code/javascript/functions/vscode/CancellationToken/isCancellationRequested if (cancelToken) { } // silence our compiler for now... TODO: we should adjust loop so it can break on cancelToken.isCancellationRequested + this._resetForNewDocument(); + this._logMessage("* Config: spinExtensionBehavior.highlightFlexspinDirectives: [" + this.configuration.highlightFlexspin + "]"); + const allTokens = this._parseText(document.getText()); const builder = new vscode.SemanticTokensBuilder(); - this._resetForNewDocument(); allTokens.forEach((token) => { builder.push(token.line, token.startCharacter, token.length, this._encodeTokenType(token.ptTokenType), this._encodeTokenModifiers(token.ptTokenModifiers)); }); @@ -257,6 +261,8 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman // list of directives found in file private fileDirectives: ISpin2Directive[] = []; + private configuration = semanticConfiguration; + private currentMethodName: string = ""; private bRecordTrailingComments: boolean = false; // initially, we don't generate tokens for trailing comments on lines @@ -278,6 +284,9 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman this.currentMethodName = ""; this.debugDisplays.clear(); this.fileDirectives = []; + if (reloadSemanticConfiguration()) { + this.configuration = semanticConfiguration; + } } private _encodeTokenModifiers(strTokenModifiers: string[]): number { @@ -321,8 +330,9 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman for (let i = 0; i < lines.length; i++) { const line = lines[i]; const trimmedLine = line.trim(); - const trimmedNonCommentLine = this._getNonCommentLineRemainder(0, line); + const trimmedNonCommentLine: string = this._getNonCommentLineRemainder(0, line); const sectionStatus = this._isSectionStartLine(line); + const lineParts: string[] = trimmedNonCommentLine.split(/[ \t]/); if (currState == eParseState.inMultiLineComment) { // in multi-line non-doc-comment, hunt for end '}' to exit let closingOffset = line.indexOf("}"); @@ -341,6 +351,9 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman } // DO NOTHING Let Syntax highlighting do this continue; + } else if (this._isFlexspinPreprocessorDirective(lineParts[0])) { + this._getPreProcessor_Declaration(0, i + 1, line); + continue; } else if (sectionStatus.isSectionStart) { currState = sectionStatus.inProgressStatus; this._logState("- scan ln:" + (i + 1) + " currState=[" + currState + "]"); @@ -512,6 +525,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman const line = lines[i]; const trimmedLine = line.trim(); const sectionStatus = this._isSectionStartLine(line); + const lineParts: string[] = trimmedLine.split(/[ \t]/); // TODO: UNDONE add filter which corrects for syntax inability to mark 'comments when more than one "'" present on line! //if (trimmedLine.length > 2 && trimmedLine.includes("'")) { // const partialTokenSet: IParsedToken[] = this._possiblyMarkBrokenSingleLineComment(i, 0, line); @@ -535,6 +549,13 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman currState = priorState; } // DO NOTHING Let Syntax highlighting do this + } else if (this._isFlexspinPreprocessorDirective(lineParts[0])) { + const partialTokenSet: IParsedToken[] = this._reportPreProcessorLine(i, 0, line); + partialTokenSet.forEach((newToken) => { + this._logPreProc("=> PreProc: " + this._tokenString(newToken, line)); + tokenSet.push(newToken); + }); + continue; } else if (sectionStatus.isSectionStart) { currState = sectionStatus.inProgressStatus; this._logState(" -- ln:" + (i + 1) + " currState=[" + currState + "]"); @@ -556,7 +577,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman if (line.length > 3) { const partialTokenSet: IParsedToken[] = this._reportCON_DeclarationLine(i, 3, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> CON: " + this._tokenString(newToken, line)); + this._logCON("=> CON: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -659,7 +680,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman this._logCON("- process CON line(" + (i + 1) + "): trimmedLine=[" + trimmedLine + "]"); const partialTokenSet: IParsedToken[] = this._reportCON_DeclarationLine(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> CON: " + this._tokenString(newToken, line)); + this._logCON("=> CON: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -687,7 +708,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman // process ORG line allowing label to be present const partialTokenSet: IParsedToken[] = this._reportDAT_DeclarationLine(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> DAT: " + this._tokenString(newToken, line)); + this._logDAT("=> DAT: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); @@ -697,7 +718,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman } else { const partialTokenSet: IParsedToken[] = this._reportDAT_DeclarationLine(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> DAT: " + this._tokenString(newToken, line)); + this._logDAT("=> DAT: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); } @@ -729,7 +750,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman // in DAT sections we end with FIT or just next section const partialTokenSet: IParsedToken[] = this._reportDAT_PasmCode(i, 0, line); partialTokenSet.forEach((newToken) => { - this._logOBJ("=> DAT: " + this._tokenString(newToken, line)); + this._logPASM("=> DAT: " + this._tokenString(newToken, line)); tokenSet.push(newToken); }); const lineParts: string[] = trimmedLine.split(/[ \t]/); @@ -809,6 +830,25 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman } } + private _getPreProcessor_Declaration(startingOffset: number, lineNbr: number, line: string): void { + if (this.configuration.highlightFlexspin) { + let currentOffset: number = this._skipWhite(line, startingOffset); + const nonCommentConstantLine = this._getNonCommentLineRemainder(currentOffset, line); + // get line parts - we only care about first one + const lineParts: string[] = nonCommentConstantLine.split(/[ \t=]/); + this._logPreProc(" - ln:" + lineNbr + " GetPreProcDecl lineParts=[" + lineParts + "]"); + const directive: string = lineParts[0]; + const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; + if (this._isFlexspinPreprocessorDirective(directive)) { + // check a valid preprocessor line for a declaration + if (symbolName != undefined && directive.toLowerCase() == "#define") { + this._logPreProc(" -- new PreProc Symbol=[" + symbolName + "]"); + this._setGlobalToken(symbolName, new RememberedToken("variable", ["readonly"])); + } + } + } + } + private _getCON_Declaration(startingOffset: number, lineNbr: number, line: string): void { // HAVE DIGIT_NO_VALUE = -2 ' digit value when NOT [0-9] // -or- _clkfreq = CLK_FREQ ' set system clock @@ -819,7 +859,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman const nonCommentConstantLine = this._getNonCommentLineRemainder(currentOffset, line); this._logCON(" - ln:" + lineNbr + " GetCONDecl nonCommentConstantLine=[" + nonCommentConstantLine + "]"); - const haveEnumDeclaration: boolean = nonCommentConstantLine.indexOf("#") != -1; + const haveEnumDeclaration: boolean = nonCommentConstantLine.startsWith("#"); const containsMultiAssignments: boolean = nonCommentConstantLine.indexOf(",") != -1; let statements: string[] = [nonCommentConstantLine]; if (!haveEnumDeclaration && containsMultiAssignments) { @@ -923,7 +963,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman const isDataDeclarationLine: boolean = lineParts.length > 1 && haveLabel && this._isDatStorageType(lineParts[1]) ? true : false; if (haveLabel) { const labelName: string = lineParts[0]; - if (!this._isReservedPasmSymbols(labelName) && !labelName.toUpperCase().startsWith("IF_")) { + if (!this._isReservedPasmSymbols(labelName) && !labelName.toUpperCase().startsWith("IF_") && !labelName.toUpperCase().startsWith("_RET_")) { // org in first column is not label name, nor is if_ conditional const labelType: string = isDataDeclarationLine ? "variable" : "label"; var labelModifiers: string[] = []; @@ -1067,13 +1107,94 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman } } + private _reportPreProcessorLine(lineNumber: number, startingOffset: number, line: string): IParsedToken[] { + const tokenSet: IParsedToken[] = []; + // skip Past Whitespace + let currentOffset: number = this._skipWhite(line, startingOffset); + const nonCommentConstantLine = this._getNonCommentLineRemainder(currentOffset, line); + // get line parts - we only care about first one + const lineParts: string[] = nonCommentConstantLine.split(/[ \t=]/); + this._logPreProc(" - ln:" + lineNumber + " reportPreProc lineParts=[" + lineParts + "]"); + const directive: string = lineParts[0]; + const symbolName: string | undefined = lineParts.length > 1 ? lineParts[1] : undefined; + if (this.configuration.highlightFlexspin) { + if (this._isFlexspinPreprocessorDirective(directive)) { + // record the directive + tokenSet.push({ + line: lineNumber, + startCharacter: 0, + length: directive.length, + ptTokenType: "keyword", + ptTokenModifiers: ["control", "directive"], + }); + const hasSymbol: boolean = + directive.toLowerCase() == "#define" || + directive.toLowerCase() == "#ifdef" || + directive.toLowerCase() == "#ifndef" || + directive.toLowerCase() == "#elseifdef" || + directive.toLowerCase() == "#elseifndef"; + if (hasSymbol && symbolName != undefined) { + const nameOffset = line.indexOf(symbolName, currentOffset); + this._logPreProc(" -- GLBL symbolName=[" + symbolName + "]"); + let referenceDetails: RememberedToken | undefined = undefined; + if (this._isGlobalToken(symbolName)) { + referenceDetails = this._getGlobalToken(symbolName); + this._logPreProc(" -- FOUND preProc global " + this._rememberdTokenString(symbolName, referenceDetails)); + } + if (referenceDetails != undefined) { + // record a constant declaration! + const updatedModificationSet: string[] = directive.toLowerCase() == "#define" ? this._modifiersWith(referenceDetails.modifiers, "declaration") : referenceDetails.modifiers; + tokenSet.push({ + line: lineNumber, + startCharacter: nameOffset, + length: symbolName.length, + ptTokenType: referenceDetails.type, + ptTokenModifiers: updatedModificationSet, + }); + } else if (this._isFlexspinReservedWord(symbolName)) { + // record a constant reference + tokenSet.push({ + line: lineNumber, + startCharacter: nameOffset, + length: symbolName.length, + ptTokenType: "variable", + ptTokenModifiers: ["readonly"], + }); + } else { + // record an unknown name + tokenSet.push({ + line: lineNumber, + startCharacter: nameOffset, + length: symbolName.length, + //ptTokenType: "variable", + //ptTokenModifiers: ["readonly", "missingDeclaration"], + ptTokenType: "comment", + ptTokenModifiers: ["line"], + }); + } + } + } + } else { + // DO NOTHING we don't highlight these (flexspin support not enabled) + tokenSet.push({ + line: lineNumber, + startCharacter: 0, + length: lineParts[0].length, + ptTokenType: "macro", + ptTokenModifiers: ["directive", "illegalUse"], + }); + } + + return tokenSet; + } + private _reportCON_DeclarationLine(lineNumber: number, startingOffset: number, line: string): IParsedToken[] { const tokenSet: IParsedToken[] = []; // skip Past Whitespace let currentOffset: number = this._skipWhite(line, startingOffset); const nonCommentConstantLine = this._getNonCommentLineReturnComment(lineNumber, currentOffset, line, tokenSet); - const haveEnumDeclaration: boolean = nonCommentConstantLine.indexOf("#") != -1; + const haveEnumDeclaration: boolean = nonCommentConstantLine.startsWith("#"); const containsMultiAssignments: boolean = nonCommentConstantLine.indexOf(",") != -1; this._logCON("- reportConstant haveEnum=(" + haveEnumDeclaration + "), containsMulti=(" + containsMultiAssignments + "), nonCommentConstantLine=[" + nonCommentConstantLine + "]"); let statements: string[] = [nonCommentConstantLine]; @@ -2255,7 +2376,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman argumentOffset++; minNonLabelParts++; } - if (lineParts[argumentOffset].toUpperCase().startsWith("IF_")) { + if (lineParts[argumentOffset].toUpperCase().startsWith("IF_") || lineParts[argumentOffset].toUpperCase().startsWith("_RET_")) { // skip our conditional argumentOffset++; minNonLabelParts++; @@ -2324,7 +2445,8 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman // we don't have name registered so just mark it if (namePart != ".") { // odd special case! - if (!this._isSpinReservedWord(namePart) && !this._isBuiltinReservedWord(namePart) && !this._isDebugMethod(namePart)) { + if (!this._isSpinReservedWord(namePart) && !this._isBuiltinReservedWord(namePart) && !this._isDebugMethod(namePart) && !this._isPasmModczOperand(namePart)) { + // XYZZY this._logPASM(" -- SPIN Pasm MISSING name=[" + namePart + "]"); tokenSet.push({ line: lineNumber, @@ -3302,6 +3424,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman // adjust following true/false to show specific parsing debug private spin2DebugLogEnabled: boolean = false; // WARNING (REMOVE BEFORE FLIGHT)- change to 'false' - disable before commit private showSpinCode: boolean = true; + private showPreProc: boolean = true; private showCON: boolean = true; private showOBJ: boolean = true; private showDAT: boolean = true; @@ -3329,6 +3452,12 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman } } + private _logPreProc(message: string): void { + if (this.showPreProc) { + this._logMessage(message); + } + } + private _logCON(message: string): void { if (this.showCON) { this._logMessage(message); @@ -3862,7 +3991,7 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman private _getLocalToken(tokenName: string): RememberedToken | undefined { const desiredToken: RememberedToken | undefined = this.localTokens.get(tokenName.toLowerCase()); if (desiredToken != undefined) { - this._logOBJ(" -- FND-lTOK " + this._rememberdTokenString(tokenName, desiredToken)); + this._logTokenSet(" -- FND-lTOK " + this._rememberdTokenString(tokenName, desiredToken)); } return desiredToken; } @@ -4522,6 +4651,38 @@ export class Spin2DocumentSemanticTokensProvider implements vscode.DocumentSeman return reservedStatus; } + private _isFlexspinPreprocessorDirective(name: string): boolean { + const flexspinDirectiveOfNote: string[] = ["#define", "#ifdef", "#ifndef", "#else", "#elseifdef", "#elseifndef", "#endif", "#error", "#include", "#warn", "#undef"]; + const reservedStatus: boolean = flexspinDirectiveOfNote.indexOf(name.toLowerCase()) != -1; + return reservedStatus; + } + + private _isFlexspinReservedWord(name: string): boolean { + const flexspinReservedswordsOfNote: string[] = [ + "__propeller__", + "__propeller2__", + "__p2__", + "__flexspin__", + "__spincvt__", + "__spin2pasm__", + "__spin2cpp__", + "__have_fcache__", + "__cplusplus__", + "__date__", + "__file__", + "__line__", + "__time__", + "__version__", + "__debug__", + "__output_asm__", + "__output_bytecode__", + "__output_c__", + "__output_cpp__", + ]; + const reservedStatus: boolean = flexspinReservedswordsOfNote.indexOf(name.toLowerCase()) != -1; + return reservedStatus; + } + private _isReservedPasmSymbols(name: string): boolean { const reservedPasmSymbolNames: string[] = ["org", "orgf", "orgh", "fit", "end"]; const reservedStatus: boolean = reservedPasmSymbolNames.indexOf(name.toLowerCase()) != -1;