📄 dws2syneditutils.pas
字号:
{**********************************************************************}
{ }
{ "The contents of this file are subject to the Mozilla Public }
{ License Version 1.1 (the "License"); you may not use this }
{ file except in compliance with the License. You may obtain }
{ a copy of the License at }
{ }
{ http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an }
{ "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express }
{ or implied. See the License for the specific language }
{ governing rights and limitations under the License. }
{ }
{ The Original Code is DWSUnitEditorUtils source code, released }
{ January 10, 2003 }
{ }
{ The Initial Developer of the Original Code is Mark Ericksen }
{ Portions created by Mark Ericksen are }
{ Copyright (C) 2003 Mark Ericksen, United States of America. }
{ All Rights Reserved. }
{ }
{**********************************************************************}
{ Known Issues:
-------------------------------------------------------------------------------}
unit dws2SynEditUtils;
interface
uses Windows, SysUtils, Classes, Graphics, SynEdit, SynCompletionProposal,
SynEditTypes, dws2Comp, dws2Symbols, dws2Exprs;
{ Core functions that are used for Code completion, Parameter display and Symbol hints. }
function PerformCodeCompletion(Editor: TCustomSynEdit; Prg: TProgram;
CodeProposal: TSynCompletionProposal;
UnitItemsList, UnitInserts: TStrings;
var x, y: Integer;
IncludeImages, IncludePropertyAccessors: Boolean): Boolean; overload;
function PerformParamProposal(Editor: TCustomSynEdit; Prg: TProgram;
ParamProposal: TSynCompletionProposal;
var x, y: Integer): Boolean; overload;
function PerformHintProposal(Editor: TCustomSynEdit; Prg: TProgram;
HintProposal: TSynCompletionProposal;
var x, y: Integer;
IncludeImages: Boolean): Boolean; overload;
{ Core functions that are wrapped for convenience }
function PerformCodeCompletion(Editor: TCustomSynEdit; DWScript: TDelphiWebScriptII;
CodeProposal: TSynCompletionProposal;
UnitItemsList, UnitInserts: TStrings;
var x, y: Integer;
IncludeImages, IncludePropertyAccessors: Boolean): Boolean; overload;
function PerformCodeCompletion(Editor: TCustomSynEdit; DWScript: TDelphiWebScriptII;
CodeProposal: TSynCompletionProposal;
var x, y: Integer;
IncludeImages, IncludePropertyAccessors: Boolean): Boolean; overload;
function PerformParamProposal(Editor: TCustomSynEdit; DWScript: TDelphiWebScriptII;
ParamProposal: TSynCompletionProposal;
var x, y: Integer): Boolean; overload;
function PerformHintProposal(Editor: TCustomSynEdit; DWScript: TDelphiWebScriptII;
HintProposal: TSynCompletionProposal;
var x, y: Integer;
IncludeImages: Boolean): Boolean; overload;
{ Navigation function }
procedure ToggleFromDecl2Impl(Editor: TCustomSynEdit; AProgram: TProgram);
// if a point is in a comment or a string (as defined by the highlighter)
function IsInCommentOrString(Editor: TCustomSynEdit; APoint: TPoint): Boolean;
implementation
uses dws2IDEUtils, dws2Errors, SynEditHighlighter;
{-----------------------------------------------------------------------------
Procedure: CodeProposalProcess
Author: Mark Ericksen
Date: 18-Sep-2002
Arguments: Editor: TCustomSynEdit;
Prg: TProgram; (compiled DelphiWebScript program)
CodeProposal: TSynCompletionProposal;
UnitItemsList, UnitInserts: TStrings; (List of symbols in units compiled into application)
var x, y: Integer (position of popup window)
Result: Boolean
Purpose: Return if the CodeProposal should be executed. Fill the properties
of the component based on the current position in the editor.
-----------------------------------------------------------------------------}
function PerformCodeCompletion(Editor: TCustomSynEdit; Prg: TProgram;
CodeProposal: TSynCompletionProposal; UnitItemsList, UnitInserts: TStrings;
var x, y: Integer; IncludeImages, IncludePropertyAccessors: Boolean): Boolean;
var
i, SearchStart: Integer;
SearchSymbol: TSymbol;
dispOpts: TdSyn_DisplayOptions;
Context: TContext;
ThisWordStart: TPoint; // position where this word begins
PrevWordStart: TPoint; // position where previous word start
PrevWordStop: TPoint; // position where previous word stops
tmpLineText: string;
begin
Assert(Editor <> nil, 'CodeProposalExecute - The Editor parameter must be valued.');
Assert(Prg <> nil, 'CodeProposalExecute - The Program parameter must be valued.');
Assert(CodeProposal <> nil, 'CodeProposalExecute - The CodeProposal parameter must be valued.');
Result := not IsInCommentOrString(Editor, Editor.CaretXY); // don't display if inside a comment or a string
if not Result then // if not allowed to popup,
EXIT; // don't continue
{ Set display options }
dispOpts := [doSynStyle];
if IncludeImages then // if graphics used,
Include(dispOpts, doIncludeImage); // add the graphics
CodeProposal.ClearList;
// get the previous symbol
ThisWordStart := Editor.WordStart;
PrevWordStart := Editor.PrevWordPosEx(Point(ThisWordStart.X-1, ThisWordStart.Y)); // find previous word's starting point
PrevWordStop := Editor.WordEndEx(PrevWordStart);
SearchSymbol := Prg.SymbolDictionary.FindSymbolAtPosition(ThisWordStart.X, ThisWordStart.Y);
// if no Symbol was found, search to previous symbol for context (MyClass.Somethi - Somethi won't be found, go back to class for context)
if (SearchSymbol = nil) and (ThisWordStart.Y = PrevWordStop.Y) then // this word and previous on same line
begin
tmpLineText := Editor.LineText;
SearchStart := Length(tmpLineText);
if ThisWordStart.X < SearchStart then
SearchStart := ThisWordStart.X;
// search from current word start to previous word stop (what is in between?)
for i := SearchStart downto PrevWordStop.X do
begin
// if a '.' separates the words, use the previous word as a context
if tmpLineText[i] = '.' then
begin
SearchSymbol := Prg.SymbolDictionary.FindSymbolAtPosition(PrevWordStart.X, PrevWordStart.Y);
Break;
end;
end;
end;
// if a function, look at return type for base symbol
if SearchSymbol is TFuncSymbol then
begin
if TFuncSymbol(SearchSymbol).Kind = fkFunction then
begin
if SearchSymbol is TMethodSymbol then
SearchSymbol := TMethodSymbol(SearchSymbol).Result
else
SearchSymbol := TFuncSymbol(SearchSymbol).Result;
end;
end;
// if Symbol found is a value symbol then get the variable type
if SearchSymbol is TValueSymbol then
SearchSymbol := SearchSymbol.Typ;
// if TClassSymbol
if (SearchSymbol is TClassSymbol) then
LoadClassSymbolToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TClassSymbol(SearchSymbol), True, IncludePropertyAccessors,
False, dispOpts)
// if TRecordSymbol
else if (SearchSymbol is TRecordSymbol) then
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TRecordSymbol(SearchSymbol).Members, False, True, False, dispOpts)
// if TUnitSymbol
else if (SearchSymbol is TUnitSymbol) then begin
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TUnitSymbol(SearchSymbol).Table, False, True, False, dispOpts);
if TUnitSymbol(SearchSymbol).Table is TLinkedSymbolTable then
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TLinkedSymbolTable(TUnitSymbol(SearchSymbol).Table).Parent,
False, True, False, dispOpts);
end
{ If there is no symbol found, load the full global set including context based ones }
else if (SearchSymbol = nil) then begin
{ Start by examining context, if within a procedure, list those first? }
// Check ContextMap for symbols local to the context
Context := Prg.ContextMap.FindContext(Editor.CaretX, Editor.CaretY);
while Assigned(Context) do // don't stop until no contexts to check
begin
// add symbols local to a context
if Assigned(Context.LocalTable) then
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
Context.LocalTable, False, False, False, dispOpts,
Prg, Editor.CaretX, Editor.CaretY);
// add symbols specific to a function
if Context.ParentSym is TFuncSymbol then
begin
// add function parameters to the list
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TFuncSymbol(Context.ParentSym).Params,
False, False, False, dispOpts);
// add internal variables (result, self, etc) to the list
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TFuncSymbol(Context.ParentSym).InternalParams,
False, False, False, dispOpts,
Prg, Editor.CaretX, Editor.CaretY);
// if a method, add the object's members to the list
if Context.ParentSym is TMethodSymbol then
// load this class' members to the lists
LoadClassSymbolToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
TMethodSymbol(Context.ParentSym).ClassSymbol,
True, False, IncludePropertyAccessors, dispOpts);
end;
// pop up to the next level context and continue checking
Context := Context.Parent;
end;
// add script specific items (shown at top)
LoadSymbolsToStrings(CodeProposal.ItemList, CodeProposal.InsertList,
Prg.Table, False, False{True}, IncludePropertyAccessors {False}, dispOpts,
Prg, Editor.CaretX, Editor.CaretY); // limit to symbols declared before position
{ Add unit global items (shown after script-based items) }
{ if not provided pre-built lists, then fill lists here.}
if (UnitItemsList = nil) or (UnitInserts = nil) then begin
LoadUnitDeclaredSymbols(Prg, CodeProposal.ItemList, CodeProposal.InsertList, dispOpts, [], False);
end
{ Provided pre-built lists of unit declared symbols, Append them now. }
else begin
CodeProposal.ItemList.AddStrings(UnitItemsList);
CodeProposal.InsertList.AddStrings(UnitInserts);
end;
end;
end;
{-----------------------------------------------------------------------------
Procedure: PerformParamProposal
Author: Mark Ericksen
Date: 21-Sep-2002
Arguments: Editor: TCustomSynEdit; Prg: TProgram; ParamProposal: TSynCompletionProposal; var x, y: Integer
Result: Boolean
Purpose: Will show the Param Proposal dialog filled with the data for the appropriate symbol.
-----------------------------------------------------------------------------}
function PerformParamProposal(Editor: TCustomSynEdit; Prg: TProgram;
ParamProposal: TSynCompletionProposal;
var x, y: Integer): Boolean;
var
locLine: String;
TmpX, TmpY, StartX, StartY,
TmpLocation: Integer;
FoundSymbol: TSymbol;
p: Integer;
ParamText: string;
tmpPoint: TPoint;
ParamDelim: string;
{ Related to paren matching (taken from SynEdit.pas GetMatchingBracketEx() }
Test: Char;
NumBrackets: Integer;
isCommentOrString: Boolean;
thisParam: string;
begin
Assert(Assigned(Editor.Highlighter), 'A highligher must be assigned to the editor.');
with Editor do
begin
{ We start by decrementing X, by adding a ' ' to then end of the line it makes it so
we don't lose any meaningful data when a line reads like this "thing," because
the last character is a ',' (it would have been skipped by first dec(X). }
locLine := LineText + ' ';
//go back from the cursor and find the first open paren
TmpX := CaretX;
TmpY := CaretY;
if TmpX > Length(locLine) then
TmpX := Length(locLine)
else if TmpX = 1 then
TmpX := 2; // immediately gets decremented (allows it to work when at beginning of line)
NumBrackets := 0;
FoundSymbol := nil;
TmpLocation := 0;
dec(TmpX);
while (TmpX > 0) and (TmpY > 0) and (FoundSymbol = nil) do
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -