Discussion:
IOTACodeInsightManager
(too old to reply)
m***@gmail.com
2007-04-04 03:49:15 UTC
Permalink
I've written a code insight manager, and I'm so freaking close to
breaking the learning barrier and finally understanding how to work
it, but I don't really understand a couple things. If someone could
offer a little advice, I'd appreciate it.

I've got a shell of a code manager written, and I've got it to the
point where it will call "getHelpText", or "invoke[x]". The problem
is that when it calls those methods, nothing happens. I know it gets
in there because I tossed a showMessage() in there and the message
pops up, but when I set result := 'HOLY MOLY!" for getHelpText, no
help text appears. Am I missing something? LIke a snippet of code
that will cause the actual insight to be displayed?

I've googled like crazy, but have found very little information about
this, and delphi's documentation is lacking.

Also, any advice on how I can do the following would be appreciated:

1) Mouse over tooltip help(not key invoked)
2) Building the list of parameters. The parameter list object seems
to be lacking methods to add items.

Thanks in advance.
m***@gmail.com
2007-04-09 20:08:24 UTC
Permalink
Is anyone out there able to offer any advice at all? I am absolutely
out of places to look for information. Someone out there has to have
at least fooled around with this a little bit with some success.
Below is my source. I'm trying to create a editor for a file format
specific to the company that I work for. I want to use delphi's
editor because we have copies of delphi licensed, and the syntax
highlighting is lightning fast, and the editor's features are nice as
well. You can ignore the showmessages, I was just trying to get a
feel for the order of events to see what was getting fired and when.
Basically, at this point, I just want to hit "=" and have a message
appear as hint text.

unit ESP_Highlighter;
interface

uses
Windows, SysUtils, Classes, ToolsAPI, Dialogs,
ESP_TemplateControlForm;

const scSection = 1;
const scParameter = 2;
const scDBField = 3;

var
codeManagerIndex : integer;
// ciManager : IOTACodeInsightManager;
type

TSimpleHighlight = class(TNotifierObject, IUnknown, IOTANotifier,
IOTAHighlighter, IOTAWizard)
private


public

function GetIDString: string;
function GetName: string;
procedure Tokenize(StartClass: TOTALineClass; LineBuf: POTAEdChar;
LineBufLen: TOTALineSize; HighlightCodes: POTASyntaxCode);
function TokenizeLineClass(StartClass: TOTALineClass;
LineBuf: POTAEdChar; LineBufLen: TOTALineSize): TOTALineClass;
function GetState: TWizardState;
procedure Execute;
constructor Create;
procedure TCF(const Context: IOTAKeyContext; KeyCode: TShortcut;
var BindingResult: TKeyBindingResult);
var
templateControlForm : TTemplateControl_Frm;
end;

TESPCodeComplete=class(TNotifierObject, IOTACodeInsightManager)
published
public
editView : IOTAEditView;

function GetIDString: string;
function GetName: string;

{ returns whether we should be able to be invoked or not }
function GetEnabled: Boolean;
{ sets the active state to Value so this manager may be turned
off }
procedure SetEnabled(Value: Boolean);
{
returns a charset used to get the token at the current editor
position. This is
used for retrieving the seed text when code completion is
invoked as well as
retrieving the token from the editor when we are typing for look
ahead.
The PreValidating parameter should be used to add special tokens
to the charset for retrieval
from the editor. For instance, C++ might add ['.', '-', '>'] to
the returned charset
when it is prevalidating.
}
function EditorTokenValidChars(PreValidating: Boolean):
TSysCharSet;
{
the implementor should set Allow to True if it wishes to be
invoked for the key 'Key'.
'Key' is the key which the user pressed to invoke code
completion.
There are four special values to 'Key' when invoked by the code
insight timer.

They are as follows:
#0 : Code completion was requested.
#1 : Parameter insight was requested.
#2 : A browse was requested.
#3 : a symbol hint was requested.
}
procedure AllowCodeInsight(var Allow: Boolean; const Key: Char);
{
the implementor should return true if it wishes to allow the
token 'str' to be
a valid code point for Code Insight.
}
function PreValidateCodeInsight(const Str: string): Boolean;
{ returns whether the symbol at index 'Index' as browseable in the
Code completion viewer }
function IsViewerBrowsable(Index: Integer): Boolean;
{ returns whether the code completion viewer allows multi-select }
function GetMultiSelect: Boolean;
{ returns the symbol list to the caller }
procedure GetSymbolList(out SymbolList:
IOTACodeInsightSymbolList);
{
determines whether or not the key 'Key' which was entered into
the editor should close
the code completion viewer or not (set CloseViewer to True or
False depending on your choice).
Also, the implementor should inform the manager whether or not
it should accept the symbol
at the currently selected index/indices.
}
procedure OnEditorKey(Key: Char; var CloseViewer: Boolean; var
Accept: Boolean);
{ returns true if this manager should handle this file }
function HandlesFile(const AFileName: string): Boolean;
{ returns the longest symbol class text for measurement for the
viewer. i.e. 'constructor' is longer than 'var' }
function GetLongestItem: string;
{ returns a parameter list to the manager }
procedure GetParameterList(out ParameterList:
IOTACodeInsightParameterList);
{
given key 'AChar' which was entered into the editor and the
current element (atComment, atIdentifier, etc),
return how code insight should be invoked and which type of
invocation it should be.

As an example, GetCodeInsightType() might be implemented
something like this:
...
begin
InvokeType := itManual;
if not ((AElement = atString) and (AChar <> #1)) and not
(AElement = atComment) then
begin
case AChar of
#0: CodeInsightType := citCodeInsight;
#1: CodeInsightType := citParameterCodeInsight;
#2: CodeInsightType := citBrowseCodeInsight;
#3: CodeInsightType := citHintCodeInsight;
'.':
begin
CodeInsightType := citCodeInsight;
InvokeType := itTimer;
end;
'(':
begin
CodeInsightType := citParameterCodeInsight;
InvokeType := itTimer;
end;
end;
end
else
CodeInsightType := citNone;
end;
}
procedure GetCodeInsightType(AChar: Char; AElement: Integer; out
CodeInsightType: TOTACodeInsightType;
out InvokeType: TOTAInvokeType);
{
returns true if invocation was successful. HowInvoked informs
the implementor whether
it was invoked via timer, manual, etc... Str is the text to
seed to viewer with and
is used for the initial filtering in the viewer.
}
function InvokeCodeCompletion(HowInvoked: TOTAInvokeType; var Str:
string): Boolean;
{
returns true if invocation was successful. HowInvoked informs
the implementor whether
it was invoked via timer, manual, etc... SelectedIndex is the
index of the current parameter
for the method/proc.
}
function InvokeParameterCodeInsight(HowInvoked: TOTAInvokeType;
var SelectedIndex: Integer): Boolean;
{
tells the manager where it should anchor the parameter hint
window.
A default value (EdPos) is provided for the implementor to
change if they so wish.
}
procedure ParameterCodeInsightAnchorPos(var EdPos: TOTAEditPos);
{
returns the index of the parameter which should be highlighted
based upon EdPos.
This is used to reduce extra codeinsight invocations as an
implementor might
store off the editor positions of parameters on the first
invocation.
return a -1 if you want to be reinvoked.
}
function ParameterCodeInsightParamIndex(EdPos: TOTAEditPos):
Integer;
{ return the hint string for the position in the editor (HintLine/
HintCol are the editor coordinates) }
function GetHintText(HintLine, HintCol: Integer): string;
{
return a FileName and LineNumber for the symbol which is
requested to be browsed to.
if Index > -1 then it is an index into the symbol list and the
browse was requested
by a user clicking in the code completion viewer.
return false if you'd like to inform the user that the requested
operation failed otherwise return true.
if you wish to fail by not informing the user, set AFileName =
'' and ALineNum = 0.
if Index is -1, you should use the global CodeInsightServices()
and request the EditView from it.
This should be able to give you any information you require.
}
function GotoDefinition(out AFileName: string; out ALineNum:
Integer; Index: Integer = -1): Boolean;
{
called when the code completion is completed. Accepted is true
if the user has requested
the item hinted to them in the viewer otherwise Accepted is
false.
DisplayParams should be set to true if the implementor would
like to be requeried
for parameter invocation. It is up to the implementor to insert
the text into the editor.
One way might be to use
CodeInsightServices.InsertText(StrToInsert, ShouldReplace);
Another might be to acquire the EditView from
CodeInsightServices.GetEditView() and do
the insertion yourself.
}
procedure Done(Accepted: Boolean; out DisplayParams: Boolean);
property Name: string read GetName;
property MultiSelect: Boolean read GetMultiSelect;
property Enabled: Boolean read GetEnabled write SetEnabled;
function GetOptionSetName: string;
function GetHelpInsightHtml: WideString;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterPackageWizard(TSimpleHighlight.Create);
end;

{ TSimpleHighlight }


procedure TESPCodeComplete.AllowCodeInsight(var Allow: Boolean;
const Key: Char);
begin
allow := true;
end;

constructor TSimpleHighlight.Create;
begin
inherited;
(BorlandIDEServices as IOTAHighlightServices).AddHighlighter(Self);
end;


procedure TSimpleHighlight.Execute;
begin

end;

procedure TESPCodeComplete.Done(Accepted: Boolean; out DisplayParams:
Boolean);
begin
showMessage('done');
inherited;
end;

function TESPCodeComplete.EditorTokenValidChars(
PreValidating: Boolean): TSysCharSet;
begin
result := ['.','='];
inherited;
end;

procedure TESPCodeComplete.GetCodeInsightType(AChar: Char; AElement:
Integer;
out CodeInsightType: TOTACodeInsightType; out InvokeType:
TOTAInvokeType);
begin
InvokeType := itManual;

if aElement = atReservedWord then
begin
CodeInsightType := citHintCodeInsight;
end;
end;

function TESPCodeComplete.GetEnabled: Boolean;
begin
result := true;
inherited;
end;

function TESPCodeComplete.GetHelpInsightHtml: WideString;
begin
result := 'hi';
end;

function TESPCodeComplete.GetHintText(HintLine, HintCol: Integer):
string;
begin
inherited;
result := '<html><body>hi!</body></html>';
showmessage(result);
inherited;
end;

function TSimpleHighlight.GetIDString: string;
begin
Result := 'psv.execStoredprocHighlight';
end;

function TESPCodeComplete.GetIDString: string;
begin
result := 'psv.execStoredprocCodeComplete';
end;

function TESPCodeComplete.GetLongestItem: string;
begin
result := 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
inherited;
end;

function TESPCodeComplete.GetMultiSelect: Boolean;
begin
result := false;
inherited;
end;

function TESPCodeComplete.GetName: string;
begin
result := 'execStoredProcCodeComplete';
inherited;
end;

function TSimpleHighlight.GetName: string;
begin
Result := 'execStoredProc';
end;


function TESPCodeComplete.GetOptionSetName: string;
begin
//result := 'execStoredProc ';
inherited;
end;

procedure TESPCodeComplete.GetParameterList(
out ParameterList: IOTACodeInsightParameterList);
begin
showMessage('getParameterList');
inherited;
//parameterList := TESPCodeCompleteParameterList.Create;
end;

function TSimpleHighlight.GetState: TWizardState;
begin
Result := [wsEnabled];
end;

procedure TESPCodeComplete.GetSymbolList(
out SymbolList: IOTACodeInsightSymbolList);
begin

inherited;
end;

function TESPCodeComplete.GotoDefinition(out AFileName: string;
out ALineNum: Integer; Index: Integer): Boolean;
begin
showMessage('gotoDefinition');
result := true;
inherited;
end;

function TESPCodeComplete.HandlesFile(const AFileName: string):
Boolean;
begin
if upperCase(extractFileExt(aFileName))='.WSP' then
begin
result := true;
end
else
result := false;
inherited;
end;

function TESPCodeComplete.InvokeCodeCompletion(HowInvoked:
TOTAInvokeType;
var Str: string): Boolean;
begin
str := 'hi';
showMessage('invokeCodeCompletion');
result := true;
inherited;
end;

function TESPCodeComplete.InvokeParameterCodeInsight(HowInvoked:
TOTAInvokeType;
var SelectedIndex: Integer): Boolean;
begin
showMessage('invokeParameterCodeInsight');
result := true;
inherited;
end;

function TESPCodeComplete.IsViewerBrowsable(Index: Integer): Boolean;
begin
showMessage('isViewerBrowsable');
result := true;
inherited;
end;

procedure TESPCodeComplete.OnEditorKey(Key: Char; var CloseViewer,
Accept: Boolean);
begin
showMessage('onEditorKey');
inherited;
end;

procedure TESPCodeComplete.ParameterCodeInsightAnchorPos(
var EdPos: TOTAEditPos);
begin
showMessage('parameterCodeInsightAnchorPos');
inherited;
end;

function TESPCodeComplete.ParameterCodeInsightParamIndex(
EdPos: TOTAEditPos): Integer;
begin
showMessage('ParameterCodeInsightParamIndex');
result := 0;
inherited;
end;

function TESPCodeComplete.PreValidateCodeInsight(const Str: string):
Boolean;
begin
result := true;
showMessage('preValidateCodeInsight' + str);
inherited;
end;

procedure TESPCodeComplete.SetEnabled(Value: Boolean);
begin
showMessage('setEnabled');
inherited;
end;

procedure TSimpleHighlight.TCF(const Context: IOTAKeyContext; KeyCode:
TShortcut; var BindingResult: TKeyBindingResult);
begin
showmessagE('hi');
end;

procedure TSimpleHighlight.Tokenize(StartClass: TOTALineClass;
LineBuf: POTAEdChar; LineBufLen: TOTALineSize;
HighlightCodes: POTASyntaxCode);
var
Codes : PChar;
i : integer;
begin
Codes := PChar(HighlightCodes);
FillChar(HighlightCodes^, LineBufLen, $E);
for i := 0 to LineBufLen -1 do
begin
if StartClass = scDBField then
begin
Codes[i] := Char(atIdentifier);

if (LineBuf[i] = '}') then
StartClass := 0;
end
else if startClass=scSection then
begin
codes[i] := char(atReservedWord);
if (lineBuf[i] = ']') then
startClass := 0;

end
else
begin
if ( LineBuf[i] in ['0'..'9'] ) then
Codes[i] := Char(atNumber);

if (lineBuf[i] = '[') then
begin
codes[i] := char(atReservedWord);
startClass := scSection;
end;

// if (lineBuf[i]='$') and (startClass = 0) then
// begin
// codes[i] := char(atIdentifier);
// startClass := 1;
// end;

if LineBuf[i] = '{' then
begin
Codes[i] := Char(atIdentifier);
StartClass := scDBField;
end;

end;
end;
end;

function TSimpleHighlight.TokenizeLineClass(StartClass: TOTALineClass;
LineBuf: POTAEdChar; LineBufLen: TOTALineSize): TOTALineClass;
var
i : integer;
begin
Result := StartClass;
for i := 0 to LineBufLen - 1 do
begin
case LineBuf[i] of
'{' : Result := 1;
'}': Result := 0;
end;
end;
end;

{ TESPCodeCompleteParameterList }


initialization
codeManagerIndex := (BorlandIDEServices as
IOTACodeInsightServices).AddCodeInsightManager(TESPCodecomplete.create);
finalization
(borlandIDEServices as
IOTACodeInsightServices).RemoveCodeInsightManager(codeManagerIndex);
end.
Adam Markowitz
2007-04-13 00:25:19 UTC
Permalink
Matt,
1. Tooltip mouse overs are supplied by the text returned by
IOTACodeInsightManager::GetHintText().
2. If you want to support the Help Insight mouse over hints your manager
must also implement the correct inteface (I don't have ToolsAPI.pas in front
of me, this is all by memory). I think it has a method called
GetHelpInsightHtml() or some such.
3. Your manager has a method to return an IOTACodeInsightParameterList
which is something that you implement. If you populate your
IOTACodeInsightSymbolList and/or your IOTACodeInsightParameterList during
the InvokeCodeInsight() method the IDE will call your manager back to
retrieve those lists (look for the methods with out params of those
interface types).

HTH and feel free to post other questions or drop me an email (I drop in
here every once in a while). I'm pretty familiar with the code insight
managers and their IDE interaction :)

-Adam
Post by m***@gmail.com
I've written a code insight manager, and I'm so freaking close to
breaking the learning barrier and finally understanding how to work
it, but I don't really understand a couple things. If someone could
offer a little advice, I'd appreciate it.
I've got a shell of a code manager written, and I've got it to the
point where it will call "getHelpText", or "invoke[x]". The problem
is that when it calls those methods, nothing happens. I know it gets
in there because I tossed a showMessage() in there and the message
pops up, but when I set result := 'HOLY MOLY!" for getHelpText, no
help text appears. Am I missing something? LIke a snippet of code
that will cause the actual insight to be displayed?
I've googled like crazy, but have found very little information about
this, and delphi's documentation is lacking.
1) Mouse over tooltip help(not key invoked)
2) Building the list of parameters. The parameter list object seems
to be lacking methods to add items.
Thanks in advance.
Loading...