456 lines
10 KiB
C
456 lines
10 KiB
C
|
#include <AEDataModel.h>
|
|||
|
|
|||
|
#define DEBUG 0
|
|||
|
|
|||
|
#define kComponentSignatureString "BBPy.LM"
|
|||
|
#include <Debugging.h>
|
|||
|
|
|||
|
|
|||
|
#include <BBLMInterface.h>
|
|||
|
#include <BBXTInterface.h>
|
|||
|
//#include <BBLMTextIterator.h>
|
|||
|
|
|||
|
#include <ctype.h>
|
|||
|
#include <string.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdarg.h>
|
|||
|
|
|||
|
#include <Sound.h>
|
|||
|
|
|||
|
#if DEBUG
|
|||
|
void debugf_(const char* func,const char* fileName,long line, const char*fmt,...)
|
|||
|
{
|
|||
|
va_list arg;
|
|||
|
char msg[256];
|
|||
|
va_start(arg, fmt);
|
|||
|
vsnprintf(msg,256 ,fmt, arg);
|
|||
|
DebugAssert(COMPONENT_SIGNATURE, DEBUG_NO_OPTIONS, kComponentSignatureString ": " , msg, nil, fileName, line, 0 );
|
|||
|
|
|||
|
//debug_string(msg);
|
|||
|
}
|
|||
|
#define debugf(FMT,...) debugf_( __FUNCTION__,__FILE__, __LINE__,FMT,__VA_ARGS__);
|
|||
|
#else
|
|||
|
#define debugf(FMT,...)
|
|||
|
#endif
|
|||
|
|
|||
|
typedef const char *Str;
|
|||
|
|
|||
|
|
|||
|
enum{
|
|||
|
kPyBBLMStringSubst = kBBLMFirstUserRunKind
|
|||
|
};
|
|||
|
|
|||
|
#define iswordchar(x) (isalnum(x)||x=='_')
|
|||
|
|
|||
|
|
|||
|
struct runloc{
|
|||
|
bool past_gap;
|
|||
|
long pos;
|
|||
|
long last_start;
|
|||
|
unsigned char*p;
|
|||
|
};
|
|||
|
|
|||
|
char start(struct runloc& r,BBLMParamBlock &pb)
|
|||
|
{
|
|||
|
r.past_gap = false;
|
|||
|
r.last_start = pb.fCalcRunParams.fStartOffset;
|
|||
|
r.pos = pb.fCalcRunParams.fStartOffset;
|
|||
|
r.p = ((unsigned char*)pb.fText) + pb.fCalcRunParams.fStartOffset;
|
|||
|
// Adjust for the gap if we<77>re not already past it.
|
|||
|
if ((!r.past_gap) && (r.pos >= pb.fTextGapLocation)){
|
|||
|
r.p += pb.fTextGapLength;
|
|||
|
r.past_gap = true;
|
|||
|
}
|
|||
|
return *r.p;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
char nextchar(struct runloc&r,BBLMParamBlock &pb)
|
|||
|
{
|
|||
|
if ( r.pos< pb.fTextLength){
|
|||
|
++r.pos;
|
|||
|
++r.p;
|
|||
|
if ((!r.past_gap) && (r.pos >= pb.fTextGapLocation)){
|
|||
|
r.p += pb.fTextGapLength;
|
|||
|
r.past_gap = true;
|
|||
|
}
|
|||
|
return *r.p;
|
|||
|
}
|
|||
|
else{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool addRun(BBLMRunCode kind, int start,int len , const BBLMCallbackBlock& bblm_callbacks)
|
|||
|
{
|
|||
|
if (len > 0){ // Tie off the code run we were in, unless the length is zero.
|
|||
|
debugf("Run %d %d:%d", kind, start, start+len-1 );
|
|||
|
return bblmAddRun( &bblm_callbacks, 'Pyth',
|
|||
|
kind, start, len, false);
|
|||
|
|
|||
|
}
|
|||
|
else{
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool addRunBefore (BBLMRunCode kind,struct runloc& r, const BBLMCallbackBlock& bblm_callbacks)
|
|||
|
{
|
|||
|
bool more_runs = addRun(kind, r.last_start, r.pos - r.last_start, bblm_callbacks);
|
|||
|
r.last_start = r.pos;
|
|||
|
return more_runs;
|
|||
|
}
|
|||
|
|
|||
|
bool addRunTo (BBLMRunCode kind, struct runloc& r, const BBLMCallbackBlock& bblm_callbacks)
|
|||
|
{
|
|||
|
bool more_runs = addRun(kind, r.last_start, r.pos - r.last_start+1, bblm_callbacks);
|
|||
|
r.last_start = r.pos+1;
|
|||
|
return more_runs;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
bool colorstr( char delim,
|
|||
|
BBLMParamBlock &pb,
|
|||
|
struct runloc &r,
|
|||
|
const BBLMCallbackBlock &bblm_callbacks)
|
|||
|
{
|
|||
|
bool tripple = false , pers = false, lookup = false, more_runs = true;
|
|||
|
char c = nextchar(r,pb);
|
|||
|
|
|||
|
if (c == delim){
|
|||
|
c = nextchar(r,pb);
|
|||
|
if (c == delim){
|
|||
|
tripple = true;
|
|||
|
c = nextchar(r,pb);
|
|||
|
}
|
|||
|
else{
|
|||
|
//double
|
|||
|
return addRunBefore(kBBLMRunIsSingleString,r,bblm_callbacks);
|
|||
|
}
|
|||
|
}
|
|||
|
while (c && more_runs){
|
|||
|
if (pers ){
|
|||
|
if (isalpha(c)){
|
|||
|
more_runs = addRunTo(kPyBBLMStringSubst,r,bblm_callbacks);
|
|||
|
}
|
|||
|
else if (c == '('){
|
|||
|
lookup = true;
|
|||
|
}
|
|||
|
}
|
|||
|
pers = false;
|
|||
|
if (c == delim){
|
|||
|
if (tripple){
|
|||
|
if ((c = nextchar(r,pb))== delim && (c = nextchar(r,pb)) == delim){
|
|||
|
break; // end of tripple-quote.
|
|||
|
}
|
|||
|
}
|
|||
|
else{
|
|||
|
break; // end of single-quote.
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
else if (c== '\\'){
|
|||
|
nextchar(r,pb);
|
|||
|
}
|
|||
|
else if (c=='\r'||c=='\n'){
|
|||
|
if (!tripple){
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (c=='%'){
|
|||
|
more_runs = addRunBefore(kBBLMRunIsSingleString,r,bblm_callbacks);
|
|||
|
pers = true;
|
|||
|
}
|
|||
|
else if (c==')' && lookup){
|
|||
|
more_runs = addRunTo(kPyBBLMStringSubst,r,bblm_callbacks);
|
|||
|
lookup = false;
|
|||
|
}
|
|||
|
c = nextchar(r,pb);
|
|||
|
}
|
|||
|
return more_runs && addRunTo(lookup?kPyBBLMStringSubst:kBBLMRunIsSingleString,r,bblm_callbacks);
|
|||
|
}
|
|||
|
|
|||
|
bool colorcomment(BBLMParamBlock &pb,
|
|||
|
struct runloc &r,
|
|||
|
const BBLMCallbackBlock &bblm_callbacks)
|
|||
|
{
|
|||
|
while (char c = nextchar(r,pb)){
|
|||
|
if (c=='\r'|| c=='\n'){
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return addRunTo(kBBLMRunIsLineComment,r,bblm_callbacks);
|
|||
|
}
|
|||
|
|
|||
|
void CalculateRuns(BBLMParamBlock &pb,
|
|||
|
const BBLMCallbackBlock &bblm_callbacks)
|
|||
|
|
|||
|
{
|
|||
|
const struct rundesc *state = NULL;
|
|||
|
bool more_runs=true;
|
|||
|
|
|||
|
struct runloc r;
|
|||
|
|
|||
|
char c = start(r,pb);
|
|||
|
|
|||
|
while (c && more_runs){
|
|||
|
loop:
|
|||
|
// Process a char
|
|||
|
if (state==NULL){
|
|||
|
//If we're in the basic 'code' state, check for each interesting char (rundelims[i].start).
|
|||
|
switch (c){
|
|||
|
case '\'':
|
|||
|
case '"':
|
|||
|
more_runs = addRunBefore(kBBLMRunIsCode,r,bblm_callbacks);
|
|||
|
if (more_runs){
|
|||
|
more_runs = colorstr(c,pb,r,bblm_callbacks);
|
|||
|
}
|
|||
|
break;
|
|||
|
case '#' :
|
|||
|
more_runs = addRunBefore(kBBLMRunIsCode,r,bblm_callbacks);
|
|||
|
if (more_runs){
|
|||
|
more_runs = colorcomment(pb,r,bblm_callbacks);
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
c = nextchar(r,pb);
|
|||
|
}
|
|||
|
if (more_runs){
|
|||
|
addRunBefore(kBBLMRunIsCode,r,bblm_callbacks);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
static void AdjustRange(BBLMParamBlock ¶ms,
|
|||
|
const BBLMCallbackBlock &callbacks)
|
|||
|
{
|
|||
|
DescType language;
|
|||
|
BBLMRunCode kind;
|
|||
|
SInt32 charPos;
|
|||
|
SInt32 length;
|
|||
|
UInt32 index = params.fAdjustRangeParams.fStartIndex;
|
|||
|
|
|||
|
while( index > 0 &&
|
|||
|
bblmGetRun(&callbacks, index, language, kind, charPos, length) &&
|
|||
|
(kind==kPyBBLMStringSubst||kind==kBBLMRunIsSingleString)){
|
|||
|
index--;
|
|||
|
};
|
|||
|
params.fAdjustRangeParams.fStartIndex = index;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// The next couple funcs process the text of a file assumming it's in 1 piece in memory,
|
|||
|
// so they may not be called from CalculateRuns.
|
|||
|
|
|||
|
bool matchword(BBLMParamBlock &pb, const char *pat ,unsigned long *pos)
|
|||
|
{
|
|||
|
const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
|
|||
|
|
|||
|
int i;
|
|||
|
for (i=0; pat[i]; i++){
|
|||
|
if (*pos+i>=pb.fTextLength){
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (asciText[*pos+i] != pat[i]){
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
if ((*pos+i<pb.fTextLength)&&iswordchar(asciText[*pos+i])){
|
|||
|
return false;
|
|||
|
}
|
|||
|
*pos+=i;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
int matchindent(BBLMParamBlock &pb, UInt32 *pos)
|
|||
|
{
|
|||
|
const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
|
|||
|
int indent=0;
|
|||
|
|
|||
|
while(*pos<pb.fTextLength){
|
|||
|
switch (/*(char)(pb.fTextIsUnicode?uniText[pos]:*/asciText[*pos]/*)*/){
|
|||
|
case ' ':
|
|||
|
++*pos;
|
|||
|
indent++;
|
|||
|
break;
|
|||
|
case '\t':
|
|||
|
++*pos;
|
|||
|
indent+=8;
|
|||
|
break;
|
|||
|
case '#':
|
|||
|
return -1;
|
|||
|
break;
|
|||
|
default:
|
|||
|
return indent;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void eat_line(BBLMParamBlock &pb, unsigned long* pos)
|
|||
|
{
|
|||
|
const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
|
|||
|
while (asciText[*pos]!='\r' && asciText[*pos]!='\n' && *pos<pb.fTextLength) {++*pos;}
|
|||
|
while ((asciText[*pos]=='\r' || asciText[*pos]=='\n') && *pos<pb.fTextLength) {++*pos;}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
void addItem(BBLMParamBlock &pb, UInt32 pos, int nest, BBLMFunctionKinds kind,
|
|||
|
const BBLMCallbackBlock *bblm_callbacks)
|
|||
|
{
|
|||
|
UInt32 funcstartpos = pos;
|
|||
|
UInt32 funcnamelen=0;
|
|||
|
UInt32 offset=0;
|
|||
|
const char *asciText = (const char *) pb.fText;
|
|||
|
UInt32 index;
|
|||
|
OSErr err;
|
|||
|
|
|||
|
while (isspace(asciText[pos]) && pos<pb.fTextLength) {++pos;}
|
|||
|
UInt32 fnamestart = pos;
|
|||
|
while ((isalnum(asciText[pos])||asciText[pos]=='_') && pos<pb.fTextLength) {pos++; funcnamelen++;}
|
|||
|
|
|||
|
err = bblmAddTokenToBuffer( bblm_callbacks,
|
|||
|
pb.fFcnParams.fTokenBuffer,
|
|||
|
(void*)&asciText[fnamestart],
|
|||
|
funcnamelen,
|
|||
|
pb.fTextIsUnicode,
|
|||
|
&offset);
|
|||
|
BBLMProcInfo procInfo;
|
|||
|
procInfo.fFunctionStart = fnamestart; // char offset in file of first character of function
|
|||
|
procInfo.fFunctionEnd = pos; // char offset of last character of function
|
|||
|
|
|||
|
procInfo.fSelStart = fnamestart; // first character to select when choosing function
|
|||
|
procInfo.fSelEnd = pos; // last character to select when choosing function
|
|||
|
|
|||
|
procInfo.fFirstChar = fnamestart; // first character to make visible when choosing function
|
|||
|
|
|||
|
procInfo.fKind = kind;
|
|||
|
|
|||
|
procInfo.fIndentLevel = nest; // indentation level of token
|
|||
|
procInfo.fFlags = 0; // token flags (see BBLMFunctionFlags)
|
|||
|
procInfo.fNameStart = offset; // char offset in token buffer of token name
|
|||
|
procInfo.fNameLength = funcnamelen; // length of token name
|
|||
|
|
|||
|
err = bblmAddFunctionToList(bblm_callbacks,
|
|||
|
pb.fFcnParams.fFcnList,
|
|||
|
procInfo,
|
|||
|
&index);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
enum{
|
|||
|
maxnest=5
|
|||
|
};
|
|||
|
|
|||
|
void ScanForFunctions(BBLMParamBlock &pb,
|
|||
|
const BBLMCallbackBlock &bblm_callbacks)
|
|||
|
{
|
|||
|
|
|||
|
const char *asciText = (const char *) (pb.fTextIsUnicode?NULL:pb.fText);
|
|||
|
UniCharPtr uniText = (UniCharPtr) (pb.fTextIsUnicode?pb.fText:NULL);
|
|||
|
|
|||
|
int indents[maxnest]= {0};
|
|||
|
int nest = 0;
|
|||
|
|
|||
|
UInt32 pos=0; // current character offset
|
|||
|
|
|||
|
|
|||
|
while (pos<pb.fTextLength){
|
|||
|
|
|||
|
int indent = matchindent(pb, &pos);
|
|||
|
|
|||
|
if (indent >= 0){
|
|||
|
for (int i=0; i <= nest; i++){
|
|||
|
if (indent<=indents[i]){
|
|||
|
nest = i;
|
|||
|
indents[nest]=indent;
|
|||
|
goto x;
|
|||
|
}
|
|||
|
}
|
|||
|
indents[++nest]=indent;
|
|||
|
x:
|
|||
|
|
|||
|
if (matchword(pb,"def",&pos)){
|
|||
|
addItem( pb, pos, nest, kBBLMFunctionMark, &bblm_callbacks);
|
|||
|
}
|
|||
|
else if (matchword(pb, "class", &pos)){
|
|||
|
addItem( pb, pos, nest, kBBLMTypedef, &bblm_callbacks);
|
|||
|
}
|
|||
|
}
|
|||
|
eat_line(pb,&pos);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
OSErr main( BBLMParamBlock ¶ms,
|
|||
|
const BBLMCallbackBlock &bblm_callbacks,
|
|||
|
const BBXTCallbackBlock &bbxt_callbacks)
|
|||
|
{
|
|||
|
OSErr result;
|
|||
|
|
|||
|
if ((params.fSignature != kBBLMParamBlockSignature) ||
|
|||
|
(params.fLength < sizeof(BBLMParamBlock)))
|
|||
|
{
|
|||
|
return paramErr;
|
|||
|
}
|
|||
|
|
|||
|
switch (params.fMessage)
|
|||
|
{
|
|||
|
case kBBLMInitMessage:
|
|||
|
case kBBLMDisposeMessage:
|
|||
|
{
|
|||
|
result = noErr; // nothing to do
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case kBBLMCalculateRunsMessage:
|
|||
|
CalculateRuns(params, bblm_callbacks);
|
|||
|
result = noErr;
|
|||
|
break;
|
|||
|
|
|||
|
case kBBLMScanForFunctionsMessage:
|
|||
|
ScanForFunctions(params, bblm_callbacks);
|
|||
|
result = noErr;
|
|||
|
break;
|
|||
|
|
|||
|
case kBBLMAdjustRangeMessage:
|
|||
|
AdjustRange(params, bblm_callbacks);
|
|||
|
result = noErr;
|
|||
|
break;
|
|||
|
|
|||
|
case kBBLMMapRunKindToColorCodeMessage:
|
|||
|
switch (params.fMapRunParams.fRunKind){
|
|||
|
case kPyBBLMStringSubst:
|
|||
|
params.fMapRunParams.fColorCode = kBBLMSGMLAttributeNameColor;
|
|||
|
params.fMapRunParams.fMapped = true;
|
|||
|
break;
|
|||
|
default:
|
|||
|
params.fMapRunParams.fMapped = false;
|
|||
|
}
|
|||
|
result = noErr;
|
|||
|
break;
|
|||
|
|
|||
|
case kBBLMEscapeStringMessage:
|
|||
|
case kBBLMAdjustEndMessage:
|
|||
|
case kBBLMMapColorCodeToColorMessage:
|
|||
|
case kBBLMSetCategoriesMessage:
|
|||
|
case kBBLMMatchKeywordMessage:
|
|||
|
{
|
|||
|
result = userCanceledErr;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
result = paramErr;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|