<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
+ <TargetFramework>netcoreapp5</TargetFramework>
<OutputType>WinExe</OutputType>
<EnableDefaultItems>false</EnableDefaultItems>
-
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="CrowEditBase\CrowEditBase.csproj" />
<ProjectReference Include="plugins\CECrowDebugLog\CECrowDebugLog.csproj" >
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
- <PrivateAssets>all</PrivateAssets>
</ProjectReference>
<ProjectReference Include="plugins\CEXmlPlugin\CEXmlPlugin.csproj" >
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="plugins\CERoslynPlugin\CERoslynPlugin.csproj" >
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
- <PrivateAssets>all</PrivateAssets>
+ </ProjectReference>
+ <ProjectReference Include="plugins\CENetcoreDbgPlugin\CENetcoreDbgPlugin.csproj" >
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="/mnt/devel/CrowIDE/Crow/Crow/Crow.csproj" />
SmallUIFont = "sans, 10";
+SmallFont = "consolas, 10";
InactiveTabBackground = "DarkGrey";
SelectedTabBackground = "Onyx";
InactiveTabForeground = "Grey";
SelectedTabForeground = "White";
MenuIconSize = "14";
+ControlHighlight = "RoyalBlue";
+
+Splitter {
+ Thickness="1";
+ Background="Transparent";
+ Hover="{Background=White}";
+ Unhover="{Background=Transparent}";
+}
+DockStack {
+ Margin="0";
+ Spacing="0";
+}
DockWindow {
Template = "#CrowEdit.ui.DockWindow.template";
Background = "DarkGrey";
+ Margin="0";
}
DockingTabView {
Template = "#CrowEdit.ui.DockingTabView.template";
UseLoadingThread = "false";
}
-Editor {
- Background="White";
- Foreground="Black";
- Text="";
- Multiline="true";
-}
-
string defaultClass;
}
protected Dictionary<string, DocumentClientClassList> FileAssociations = new Dictionary<string, DocumentClientClassList> ();
+ ObservableList<LogEntry> logs = new ObservableList<LogEntry>();
+ public ObservableList<LogEntry> MainLog => logs;
+
+ public void Log(LogType type, string message) {
+ lock (logs)
+ logs.Add (new LogEntry(type, message));
+ }
+ public void ResetLog () {
+ lock (logs)
+ logs.Clear ();
+ }
public void AddFileAssociation (string extension, Type clientClass) {
if (!FileAssociations.ContainsKey (extension))
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System.Diagnostics;
+
+namespace CrowEditBase
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public class BreakPoint : CrowEditComponent
+ {
+ int index = -1;
+ public string Function;
+ string fileFullPath;
+ int line;
+ bool isEnabled;
+
+ string type;
+ string disp;
+ string warning;
+
+ public int Index {
+ get => index;
+ set {
+ if (index == value)
+ return;
+ index = value;
+ NotifyValueChanged(index);
+ }
+ }
+ public int Line {
+ get => line;
+ set {
+ if (line == value)
+ return;
+ line = value;
+ NotifyValueChanged (line);
+ }
+ }
+ public bool IsEnabled {
+ get => isEnabled;
+ set {
+ if (isEnabled == value)
+ return;
+ isEnabled = value;
+ NotifyValueChanged (isEnabled);
+ }
+ }
+ public string Type {
+ get => type;
+ set {
+ if (type == value)
+ return;
+ type = value;
+ NotifyValueChanged (type);
+ }
+ }
+ public string Disp {
+ get => disp;
+ set {
+ if (disp == value)
+ return;
+ disp = value;
+ NotifyValueChanged (disp);
+ }
+ }
+ public string Warning {
+ get => warning;
+ set {
+ if (warning == value)
+ return;
+ warning = value;
+ NotifyValueChanged (warning);
+ }
+ }
+ public string FileFullPath {
+ get => fileFullPath;
+ set {
+ if (fileFullPath == value)
+ return;
+ fileFullPath = value;
+ NotifyValueChanged (fileFullPath);
+ }
+ }
+
+ protected BreakPoint(string fileFullPath, int line, bool isEnabled = true)
+ {
+ FileFullPath = fileFullPath;
+ Line = line;
+ IsEnabled = isEnabled;
+ }
+
+ public void UpdateLocation (StackFrame frame) {
+ //FileName = frame.File;
+ FileFullPath = frame.FileFullPath;
+ Function = frame.Function;
+ Line = frame.Line - 1;
+ }
+ public override string ToString() => $"{Index}:{Type} {FileFullPath}:{Line} enabled:{IsEnabled}";
+ private string GetDebuggerDisplay() => ToString();
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Runtime.CompilerServices;
+using Crow;
+
+namespace CrowEditBase
+{
+ public abstract class Debugger : CrowEditComponent
+ {
+ public enum Status
+ {
+ /// <summary>debugger process created</summary>
+ Init,
+ /// <summary>request loading sent</summary>
+ Starting,
+ /// <summary>executable loaded, breakpoints requested</summary>
+ Ready,
+ /// <summary>running state received</summary>
+ Running,
+ /// <summary>stopped event received</summary>
+ Stopped,
+ }
+
+ protected Project project;
+ public Command CMDDebugStart, CMDDebugPause, CMDDebugStop, CMDDebugStepIn, CMDDebugStepOver, CMDDebugStepOut;
+ public virtual CommandGroup Commands => new CommandGroup (
+ CMDDebugStart, CMDDebugPause, CMDDebugStop, CMDDebugStepIn, CMDDebugStepOver, CMDDebugStepOut);
+ protected virtual void initCommands () {
+ CMDDebugStart = new Command ("Start", Start, "#Icons.debug-play.svg");
+ CMDDebugPause = new Command ("Pause", Pause, "#Icons.debug-pause.svg", false);
+ CMDDebugStop = new Command ("Stop", Stop, "#Icons.debug-stop.svg", false);
+ CMDDebugStepIn = new Command ("Step in", StepIn, "#Icons.debug-step-into.svg", false);
+ CMDDebugStepOut = new Command ("Step out", StepOut, "#Icons.debug-step-out.svg", false);
+ CMDDebugStepOver = new Command ("Step over", StepOver, "#Icons.debug-step-over.svg", false);
+ }
+
+
+ Status currentState = Status.Init;
+ bool breakOnStartup = false;
+
+ public Status CurrentState
+ {
+ get => currentState;
+ set
+ {
+ if (currentState == value)
+ return;
+ currentState = value;
+
+ CMDDebugStepIn.CanExecute = CMDDebugStepOut.CanExecute = CMDDebugStepOver.CanExecute =
+ (CurrentState == Status.Stopped);
+ CMDDebugStart.CanExecute = (CurrentState == Status.Ready || CurrentState == Status.Stopped);
+ CMDDebugPause.CanExecute = CMDDebugStop.CanExecute = (CurrentState == Status.Running);
+ }
+ }
+ StackFrame executingFile;
+ int executingLine = -1;
+
+ public ObservableList<string> OutputLog = new ObservableList<string>();
+ public ObservableList<string> ErrorLog = new ObservableList<string>();
+ public ObservableList<string> DebuggerLog = new ObservableList<string>();
+
+ public ObservableList<StackFrame> Frames = new ObservableList<StackFrame>();
+ public ObservableList<ThreadInfo> Threads = new ObservableList<ThreadInfo>();
+ public ObservableList<Watch> Watches = new ObservableList<Watch>();
+ public ObservableList<BreakPoint> BreakPoints = new ObservableList<BreakPoint>();
+
+ ThreadInfo currentThread;
+ StackFrame currentFrame;
+ BreakPoint currentBreakPoint;
+
+ public ThreadInfo CurrentThread
+ {
+ get => currentThread;
+ set
+ {
+ if (currentThread == value)
+ return;
+ currentThread = value;
+ NotifyValueChanged(currentThread);
+ }
+ }
+ public StackFrame CurrentFrame
+ {
+ get => currentFrame;
+ set
+ {
+ if (currentFrame == value)
+ return;
+ currentFrame = value;
+ NotifyValueChanged(currentFrame);
+ onCurrentFrameChanged ();
+ }
+ }
+ protected abstract void onCurrentFrameChanged ();
+ protected abstract void onCurrentThreadChanged ();
+ public BreakPoint CurrentBreakPoint
+ {
+ get => currentBreakPoint;
+ set
+ {
+ if (currentBreakPoint == value)
+ return;
+ currentBreakPoint = value;
+ NotifyValueChanged(currentBreakPoint);
+ if (currentBreakPoint == null)
+ return;
+ //tryGoTo(currentFrame);
+ }
+ }
+
+ public bool BreakOnStartup
+ {
+ get => breakOnStartup;
+ set
+ {
+ if (BreakOnStartup == value)
+ return;
+ breakOnStartup = value;
+ NotifyValueChanged(breakOnStartup);
+ }
+ }
+ public virtual Project Project
+ {
+ get => project;
+ set
+ {
+ if (project == value)
+ return;
+ project = value;
+ NotifyValueChanged(Project);
+ }
+ }
+
+ public abstract void Start();
+ public abstract void Pause();
+ public abstract void Continue();
+ public abstract void Stop();
+
+ public abstract void StepIn();
+ public abstract void StepOver();
+ public abstract void StepOut();
+
+ public abstract void InsertBreakPoint(BreakPoint bp);
+ public abstract void DeleteBreakPoint(BreakPoint bp);
+
+ protected void ResetCurrentExecutingLocation() {
+
+ }
+
+
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Runtime.CompilerServices;
+using Crow;
+
+namespace CrowEditBase
+{
+ public class DebuggerObject : IValueChange {
+ #region IValueChange implementation
+ public event EventHandler<ValueChangeEventArgs> ValueChanged;
+ public void NotifyValueChanged(string MemberName, object _value)
+ => ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value));
+
+ public void NotifyValueChanged(object _value, [CallerMemberName] string caller = null)
+ => NotifyValueChanged(caller, _value);
+ #endregion
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+
+namespace CrowEditBase
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public abstract class StackFrame
+ {
+ public int Level;
+ public string FileFullPath;
+ public int Line;
+ public int Column;
+ public int LineEnd;
+ public int ColumnEnd;
+ public string Function;
+ public string Address;
+
+ public bool IsDefined => !string.IsNullOrEmpty(FileFullPath);
+
+ public override string ToString() => $"{Level}:{FileFullPath}({Line},{Column} {Function})";
+ string GetDebuggerDisplay() => ToString();
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+
+namespace CrowEditBase
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public class ThreadInfo
+ {
+ public int Id;
+ public string Name;
+ public bool IsStopped;
+ public bool IsRunning => !IsStopped;
+
+ public override string ToString() => $"{Id}:{Name} Running:{IsRunning})";
+ string GetDebuggerDisplay() => ToString();
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+using Crow;
+
+namespace CrowEditBase
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public abstract class Watch : CrowEditComponent {
+
+ Debugger dbg;
+ bool isExpanded;
+ string name;
+ string expression;
+ string value;
+ bool isEditable;
+ int numChild;
+ string type;
+ int threadId;
+
+ ObservableList<Watch> children = new ObservableList<Watch>();
+
+ public CommandGroup Commands => new CommandGroup (
+ new Command ("Update Value", () => UpdateValue()),
+ new Command ("Delete", () => Delete())
+ );
+
+ public bool HasChildren => NumChild > 0;
+
+ public bool IsExpanded {
+ get => isExpanded;
+ set {
+ if (isExpanded == value)
+ return;
+ isExpanded = value;
+ NotifyValueChanged(isExpanded);
+
+ if (isExpanded)
+ onExpand();
+ }
+ }
+ protected abstract void onExpand();
+
+ public ObservableList<Watch> Children {
+ get => children;
+ set {
+ if (children == value)
+ return;
+ children = value;
+ NotifyValueChanged (children);
+ }
+ }
+ public string Name {
+ get => name;
+ set {
+ if (name == value)
+ return;
+ name = value;
+ NotifyValueChanged(name);
+ }
+ }
+ public string Expression {
+ get => expression;
+ set {
+ if (expression == value)
+ return;
+ expression = value;
+ NotifyValueChanged(expression);
+ }
+ }
+ public string Value {
+ get => value;
+ set {
+ if (this.value == value)
+ return;
+ this.value = value;
+ NotifyValueChanged(this.value);
+ }
+ }
+ public bool IsEditable {
+ get => isEditable;
+ set {
+ if (isEditable == value)
+ return;
+ isEditable = value;
+ NotifyValueChanged(isEditable);
+ }
+ }
+ public int NumChild {
+ get => numChild;
+ set {
+ if (numChild == value)
+ return;
+ numChild = value;
+ NotifyValueChanged(numChild);
+ NotifyValueChanged ("HasChildren", HasChildren);
+ }
+ }
+ public string Type {
+ get => type;
+ set {
+ if (type == value)
+ return;
+ type = value;
+ NotifyValueChanged(type);
+ }
+ }
+ public int ThreadId {
+ get => threadId;
+ set {
+ if (threadId == value)
+ return;
+ threadId = value;
+ NotifyValueChanged(threadId);
+ }
+ }
+
+ public abstract void Create();
+ public abstract void Delete();
+ public abstract void UpdateValue ();
+
+
+ public override string ToString() => $"{Name}:{Expression} = {Value} [{Type}]";
+ string GetDebuggerDisplay() => ToString();
+ }
+}
using System.Collections;
using CrowEditBase;
using System.Threading;
+using System.ComponentModel;
namespace Crow
{
public interface IDocumentClient {
}
- public class Editor : TextBox {
+ public class Editor : ScrollingObject, IEditableTextWidget {
#region CTOR
protected Editor () : base () {
+ KeyEventsOverrides = true;//prevent scrollingObject moves by keyboard
+
initCommands ();
+ getLines();
+
Thread t = new Thread (backgroundThreadFunc);
t.IsBackground = true;
t.Start ();
ContextCommands = new CommandGroup (CMDCut, CMDCopy, CMDPaste);
}
- public override int CurrentColumn {
- get => base.CurrentColumn;
- set {
- if (CurrentColumn == value)
- return;
- base.CurrentColumn = value;
- CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty;
- }
- }
- public override int CurrentLine {
- get => base.CurrentLine;
- set {
- if (CurrentLine == value)
- return;
- base.CurrentLine = value;
- CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty;
- }
- }
- protected override CharLocation? CurrentLoc {
- get => base.CurrentLoc;
- set {
- if (currentLoc == value)
- return;
- base.CurrentLoc = value;
- CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty;
- }
- }
+
/*protected override CharLocation? SelectionStart {
get => base.SelectionStart;
set {
RegisterForGraphicUpdate ();
}
}
- public override void OnTextChanged(object sender, TextChangeEventArgs e)
+ public event EventHandler<TextChangeEventArgs> TextChanged;
+ public virtual void OnTextChanged(object sender, TextChangeEventArgs e)
{
if (disableTextChangedEvent)
return;
- base.OnTextChanged(sender, e);
- }
- protected override void onFocused(object sender, EventArgs e)
- {
- if (CurrentLoc == null)
- CurrentLoc = new CharLocation (0, 0);
- base.onFocused(sender, e);
- (IFace as CrowEditBase.CrowEditBase).CurrentEditor = this;
+ TextChanged.Raise (this, e);
}
protected void backgroundThreadFunc () {
while (true) {
Thread.Sleep (200);
}
}
+
+ #region Label
+ protected string _text = "";
+ int targetColumn = -1;//handle line changes with long->short->long line length sequence.
+
+ protected CharLocation? hoverLoc = null;
+ protected CharLocation? currentLoc = null;
+ protected CharLocation? selectionStart = null; //selection start (row,column)
+
+ protected virtual CharLocation? CurrentLoc {
+ get => currentLoc;
+ set {
+ if (currentLoc == value)
+ return;
+ currentLoc = value;
+ NotifyValueChanged ("CurrentLine", CurrentLine);
+ NotifyValueChanged ("CurrentColumn", CurrentColumn);
+
+ CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty;
+ }
+ }
+ public virtual int CurrentLine {
+ get => currentLoc.HasValue ? currentLoc.Value.Line : 0;
+ set {
+ if (currentLoc?.Line == value)
+ return;
+ currentLoc = new CharLocation (value, currentLoc.Value.Column, currentLoc.Value.VisualCharXPosition);
+ NotifyValueChanged ("CurrentLine", CurrentLine);
+
+ CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty;
+ }
+ }
+ public virtual int CurrentColumn {
+ get => currentLoc.HasValue ? currentLoc.Value.Column < 0 ? 0 : currentLoc.Value.Column : 0;
+ set {
+ if (CurrentColumn == value)
+ return;
+ currentLoc = new CharLocation (currentLoc.Value.Line, value);
+ NotifyValueChanged ("CurrentColumn", CurrentColumn);
+
+ CMDCopy.CanExecute = CMDCut.CanExecute = !SelectionIsEmpty;
+ }
+ }
+ /// <summary>
+ /// Set current cursor position in label.
+ /// </summary>
+ /// <param name="position">Absolute character position in text.</param>
+ public void SetCursorPosition (int position) {
+ CharLocation loc = lines.GetLocation (position);
+ loc.Column = Math.Min (loc.Column, lines[loc.Line].Length);
+ CurrentLoc = loc;
+ }
+
+ Color selForeground, selBackground;
+ protected LineCollection lines;
+ protected bool textMeasureIsUpToDate = false;
+ protected object linesMutex = new object ();
+ protected string LineBreak = null;
+ protected Size cachedTextSize = default (Size);
+ protected bool mixedLineBreak = false;
+
+ protected FontExtents fe;
+ protected TextExtents te;
+
+
+ /// <summary>
+ /// Background color for selected text inside this label.
+ /// </summary>
+ [DefaultValue ("SteelBlue")]
+ public virtual Color SelectionBackground {
+ get { return selBackground; }
+ set {
+ if (selBackground == value)
+ return;
+ selBackground = value;
+ NotifyValueChangedAuto (selBackground);
+ RegisterForRedraw ();
+ }
+ }
+ /// <summary>
+ /// Selected text color inside this label.
+ /// </summary>
+ [DefaultValue("White")]
+ public virtual Color SelectionForeground {
+ get { return selForeground; }
+ set {
+ if (selForeground == value)
+ return;
+ selForeground = value;
+ NotifyValueChangedAuto (selForeground);
+ RegisterForRedraw ();
+ }
+ }
+
+
+ /// <summary>
+ /// Moves cursor one char to the left.
+ /// </summary>
+ /// <returns><c>true</c> if move succeed</returns>
+ public bool MoveLeft(){
+ //targetColumn = -1;
+ CharLocation loc = CurrentLoc.Value;
+ if (loc.Column == 0) {
+ if (loc.Line == 0)
+ return false;
+ CurrentLoc = new CharLocation (loc.Line - 1, lines[loc.Line - 1].Length);
+ }else
+ CurrentLoc = new CharLocation (loc.Line, loc.Column - 1);
+ return true;
+ }
+ public bool MoveRight () {
+ targetColumn = -1;
+ CharLocation loc = CurrentLoc.Value;
+ if (loc.Column == lines[loc.Line].Length) {
+ if (loc.Line == lines.Count - 1)
+ return false;
+ CurrentLoc = new CharLocation (loc.Line + 1, 0);
+ } else
+ CurrentLoc = new CharLocation (loc.Line, loc.Column + 1);
+ return true;
+ }
+ public bool LineMove (int lineDiff) {
+ CharLocation loc = CurrentLoc.Value;
+ int newLine = Math.Min (Math.Max (0, loc.Line + lineDiff), lines.Count - 1);
+
+ if (newLine == loc.Line)
+ return false;
+
+ if (loc.Column > lines[newLine].Length) {
+ if (targetColumn < 0)
+ targetColumn = loc.Column;
+ CurrentLoc = new CharLocation (newLine, lines[newLine].Length);
+ } else if (targetColumn < 0)
+ CurrentLoc = new CharLocation (newLine, loc.Column);
+ else if (targetColumn > lines[newLine].Length)
+ CurrentLoc = new CharLocation (newLine, lines[newLine].Length);
+ else
+ CurrentLoc = new CharLocation (newLine, targetColumn);
+
+ return true;
+ }
+ protected int visibleLines => (int)((double)ClientRectangle.Height / (fe.Ascent + fe.Descent));
+ public void GotoWordStart(){
+ int pos = lines.GetAbsolutePosition (CurrentLoc.Value);
+ //skip white spaces
+ while (pos > 0 && !char.IsLetterOrDigit (_text[pos-1]))
+ pos--;
+ while (pos > 0 && char.IsLetterOrDigit (_text[pos-1]))
+ pos--;
+ CurrentLoc = lines.GetLocation (pos);
+ }
+ public void GotoWordEnd(){
+ int pos = lines.GetAbsolutePosition (CurrentLoc.Value);
+ //skip white spaces
+ while (pos < _text.Length -1 && !char.IsLetterOrDigit (_text[pos]))
+ pos++;
+ while (pos < _text.Length - 1 && char.IsLetterOrDigit (_text[pos]))
+ pos++;
+ CurrentLoc = lines.GetLocation (pos);
+ }
+
+ protected void detectLineBreak () {
+ mixedLineBreak = false;
+
+ if (lines.Count == 0 || lines[0].LineBreakLength == 0) {
+ LineBreak = Environment.NewLine;
+ return;
+ }
+ LineBreak = _text.GetLineBreak (lines[0]).ToString ();
+
+ for (int i = 1; i < lines.Count; i++) {
+ ReadOnlySpan<char> lb = _text.GetLineBreak (lines[i]);
+ if (!lb.SequenceEqual (LineBreak)) {
+ mixedLineBreak = true;
+ break;
+ }
+ }
+ }
+
+ protected void getLines () {
+ if (lines == null)
+ lines = new LineCollection (10);
+ else
+ lines.Clear ();
+
+ if (string.IsNullOrEmpty (_text))
+ lines.Add (new TextLine (0, 0, 0));
+ else
+ lines.Update (_text);
+ }
+ /// <summary>
+ /// Current Selected text span. May be used to set current position, or current selection.
+ /// </summary>
+ public TextSpan Selection {
+ set {
+ if (value.IsEmpty)
+ selectionStart = null;
+ else
+ selectionStart = lines.GetLocation (value.Start);
+ CurrentLoc = lines.GetLocation (value.End);
+ }
+ get {
+ if (CurrentLoc == null)
+ return default;
+ CharLocation selStart = CurrentLoc.Value, selEnd = CurrentLoc.Value;
+ if (selectionStart.HasValue) {
+ if (CurrentLoc.Value.Line < selectionStart.Value.Line) {
+ selStart = CurrentLoc.Value;
+ selEnd = selectionStart.Value;
+ } else if (CurrentLoc.Value.Line > selectionStart.Value.Line) {
+ selStart = selectionStart.Value;
+ selEnd = CurrentLoc.Value;
+ } else if (CurrentLoc.Value.Column < selectionStart.Value.Column) {
+ selStart = CurrentLoc.Value;
+ selEnd = selectionStart.Value;
+ } else {
+ selStart = selectionStart.Value;
+ selEnd = CurrentLoc.Value;
+ }
+ }
+ return new TextSpan (lines.GetAbsolutePosition (selStart), lines.GetAbsolutePosition (selEnd));
+ }
+ }
+ public string SelectedText {
+ get {
+ TextSpan selection = Selection;
+ return selection.IsEmpty ? "" : _text.AsSpan (selection.Start, selection.Length).ToString ();
+ }
+ }
+ public bool SelectionIsEmpty => selectionStart.HasValue ? Selection.IsEmpty : true;
+
+ protected virtual void measureTextBounds (Context gr) {
+ fe = gr.FontExtents;
+ te = new TextExtents ();
+
+ cachedTextSize.Height = (int)Math.Ceiling ((fe.Ascent + fe.Descent) * Math.Max (1, lines.Count));
+
+ TextExtents tmp = default;
+ int longestLine = 0;
+ for (int i = 0; i < lines.Count; i++) {
+ if (lines[i].LengthInPixel < 0) {
+ if (lines[i].Length == 0)
+ lines.UpdateLineLengthInPixel (i, 0);// (int)Math.Ceiling (fe.MaxXAdvance);
+ else {
+ gr.TextExtents (_text.GetLine (lines[i]), Interface.TAB_SIZE, out tmp);
+ lines.UpdateLineLengthInPixel (i, (int)Math.Ceiling (tmp.XAdvance));
+ }
+ }
+ if (lines[i].LengthInPixel > lines[longestLine].LengthInPixel)
+ longestLine = i;
+ }
+ cachedTextSize.Width = lines[longestLine].LengthInPixel;
+ textMeasureIsUpToDate = true;
+
+ updateMaxScrolls (LayoutingType.Height);
+ updateMaxScrolls (LayoutingType.Width);
+ }
+ protected virtual void drawContent (Context gr) {
+ gr.Translate (-ScrollX, -ScrollY);
+
+ Rectangle cb = ClientRectangle;
+ fe = gr.FontExtents;
+ double lineHeight = fe.Ascent + fe.Descent;
+
+ CharLocation selStart = default, selEnd = default;
+ bool selectionNotEmpty = false;
+
+ if (HasFocus) {
+ if (currentLoc?.Column < 0) {
+ updateLocation (gr, cb.Width, ref currentLoc);
+ NotifyValueChanged ("CurrentColumn", CurrentColumn);
+ } else
+ updateLocation (gr, cb.Width, ref currentLoc);
+ if (selectionStart.HasValue) {
+ updateLocation (gr, cb.Width, ref selectionStart);
+ if (CurrentLoc.Value != selectionStart.Value)
+ selectionNotEmpty = true;
+ }
+ if (selectionNotEmpty) {
+ if (CurrentLoc.Value.Line < selectionStart.Value.Line) {
+ selStart = CurrentLoc.Value;
+ selEnd = selectionStart.Value;
+ } else if (CurrentLoc.Value.Line > selectionStart.Value.Line) {
+ selStart = selectionStart.Value;
+ selEnd = CurrentLoc.Value;
+ } else if (CurrentLoc.Value.Column < selectionStart.Value.Column) {
+ selStart = CurrentLoc.Value;
+ selEnd = selectionStart.Value;
+ } else {
+ selStart = selectionStart.Value;
+ selEnd = CurrentLoc.Value;
+ }
+ } else
+ IFace.forceTextCursor = true;
+ }
+
+ if (!string.IsNullOrEmpty (_text)) {
+ Foreground?.SetAsSource (IFace, gr);
+
+ TextExtents extents;
+ Span<byte> bytes = stackalloc byte[128];
+ double y = 0;
+
+ for (int i = 0; i < lines.Count; i++) {
+ if (!cancelLinePrint (lineHeight, y, cb.Height)) {
+ int encodedBytes = -1;
+ if (lines[i].Length > 0) {
+ int size = lines[i].Length * 4 + 1;
+ if (bytes.Length < size)
+ bytes = size > 512 ? new byte[size] : stackalloc byte[size];
+
+ encodedBytes = Crow.Text.Encoding.ToUtf8 (_text.GetLine (lines[i]), bytes);
+ bytes[encodedBytes++] = 0;
+
+ if (lines[i].LengthInPixel < 0) {
+ gr.TextExtents (bytes.Slice (0, encodedBytes), out extents);
+ lines.UpdateLineLengthInPixel (i, (int)extents.XAdvance);
+ }
+ }
+
+ RectangleD lineRect = new RectangleD (
+ (int)cb.X,
+ y + cb.Top, lines[i].LengthInPixel, lineHeight);
+
+ if (encodedBytes > 0) {
+ gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent);
+ gr.ShowText (bytes.Slice (0, encodedBytes));
+ }
+ /********** DEBUG TextLineCollection *************
+ gr.SetSource (Colors.Red);
+ gr.SetFontSize (9);
+ gr.MoveTo (700, lineRect.Y + fe.Ascent);
+ gr.ShowText ($"({lines[i].Start}, {lines[i].End}, {lines[i].EndIncludingLineBreak})");
+ gr.SetFontSize (Font.Size);
+ Foreground.SetAsSource (IFace, gr);
+ ********** DEBUG TextLineCollection *************/
+
+ if (HasFocus && selectionNotEmpty) {
+ RectangleD selRect = lineRect;
+
+ if (i >= selStart.Line && i <= selEnd.Line) {
+ if (selStart.Line == selEnd.Line) {
+ selRect.X = selStart.VisualCharXPosition + cb.X;
+ selRect.Width = selEnd.VisualCharXPosition - selStart.VisualCharXPosition;
+ } else if (i == selStart.Line) {
+ double newX = selStart.VisualCharXPosition + cb.X;
+ selRect.Width -= (newX - selRect.X) - 10.0;
+ selRect.X = newX;
+ } else if (i == selEnd.Line) {
+ selRect.Width = selEnd.VisualCharXPosition - selRect.X + cb.X;
+ } else
+ selRect.Width += 10.0;
+ } else {
+ y += lineHeight;
+ continue;
+ }
+
+ gr.SetSource (selBackground);
+ gr.Rectangle (selRect);
+ if (encodedBytes < 0)
+ gr.Fill ();
+ else {
+ gr.FillPreserve ();
+ gr.Save ();
+ gr.Clip ();
+ gr.SetSource (SelectionForeground);
+ gr.MoveTo (lineRect.X, lineRect.Y + fe.Ascent);
+ gr.ShowText (bytes.Slice (0, encodedBytes));
+ gr.Restore ();
+ }
+ Foreground.SetAsSource (IFace, gr);
+ }
+ }
+ y += lineHeight;
+ }
+ }
+
+ gr.Translate (ScrollX, ScrollY);
+ }
+ protected virtual void updateHoverLocation (Point mouseLocalPos) {
+ int hoverLine = (int)Math.Min (Math.Max (0, Math.Floor (mouseLocalPos.Y / (fe.Ascent + fe.Descent))), lines.Count - 1);
+ hoverLoc = new CharLocation (hoverLine, -1, mouseLocalPos.X);
+ using (Context gr = new Context (IFace.surf)) {
+ setFontForContext (gr);
+ updateLocation (gr, ClientRectangle.Width, ref hoverLoc);
+ }
+ }
+ protected virtual bool cancelLinePrint (double lineHeght, double y, int clientHeight) => false;
+ RectangleD? textCursor = null;
+ public virtual bool DrawCursor (Context ctx, out Rectangle rect) {
+ if (CurrentLoc == null) {
+ rect = default;
+ return false;
+ }
+ if (!CurrentLoc.Value.HasVisualX) {
+ setFontForContext (ctx);
+ lock (linesMutex) {
+ if (currentLoc?.Column < 0) {
+ updateLocation (ctx, ClientRectangle.Width, ref currentLoc);
+ NotifyValueChanged ("CurrentColumn", CurrentColumn);
+ } else
+ updateLocation (ctx, ClientRectangle.Width, ref currentLoc);
+ }
+ textCursor = null;
+ }
+
+
+ int lineHeight = (int)(fe.Ascent + fe.Descent);
+ textCursor = computeTextCursor (new RectangleD (CurrentLoc.Value.VisualCharXPosition, CurrentLoc.Value.Line * lineHeight, 1.0, lineHeight));
+
+ if (textCursor == null) {
+ rect = default;
+ return false;
+ }
+ //}
+ Rectangle c = ScreenCoordinates (textCursor.Value + Slot.Position + ClientRectangle.Position);
+ ctx.ResetClip ();
+ Foreground.SetAsSource (IFace, ctx, c);
+ ctx.LineWidth = 1.0;
+ ctx.MoveTo (0.5 + c.X, c.Y);
+ ctx.LineTo (0.5 + c.X, c.Bottom);
+ ctx.Stroke ();
+ rect = c;
+ return true;
+ }
+
+ protected void updateLocation (Context gr, int clientWidth, ref CharLocation? location) {
+ if (location == null)
+ return;
+ CharLocation loc = location.Value;
+ //Console.WriteLine ($"updateLocation: {loc} text:{_text.Length}");
+ if (loc.HasVisualX)
+ return;
+ TextLine ls = lines[loc.Line];
+ ReadOnlySpan<char> curLine = _text.GetLine (ls);
+ double cPos = 0;
+
+ if (loc.Column >= 0) {
+ //int encodedBytes = Crow.Text.Encoding2.ToUtf8 (curLine.Slice (0, loc.Column), bytes);
+#if DEBUG
+ if (loc.Column > curLine.Length) {
+ System.Diagnostics.Debug.WriteLine ($"loc.Column: {loc.Column} curLine.Length:{curLine.Length}");
+ loc.Column = curLine.Length;
+ }
+#endif
+ loc.VisualCharXPosition = gr.TextExtents (curLine.Slice (0, loc.Column), Interface.TAB_SIZE).XAdvance + cPos;
+ location = loc;
+ } else {
+ TextExtents te;
+ Span<byte> bytes = stackalloc byte[5];//utf8 single char buffer + '\0'
+
+ for (int i = 0; i < ls.Length; i++) {
+ int encodedBytes = Crow.Text.Encoding.ToUtf8 (curLine.Slice (i, 1), bytes);
+ bytes[encodedBytes] = 0;
+
+ gr.TextExtents (bytes, out te);
+ double halfWidth = te.XAdvance / 2;
+
+ if (loc.VisualCharXPosition <= cPos + halfWidth) {
+ loc.Column = i;
+ loc.VisualCharXPosition = cPos;
+ location = loc;
+ return;
+ }
+
+ cPos += te.XAdvance;
+ }
+ loc.Column = ls.Length;
+ loc.VisualCharXPosition = cPos;
+ location = loc;
+ }
+ }
+
+ protected void checkShift () {
+ if (IFace.Shift) {
+ if (!selectionStart.HasValue)
+ selectionStart = CurrentLoc;
+ } else
+ selectionStart = null;
+ }
+
+ #region GraphicObject overrides
+ public override void OnLayoutChanges (LayoutingType layoutType) {
+ base.OnLayoutChanges (layoutType);
+ updateMaxScrolls (layoutType);
+ }
+ public override bool UpdateLayout (LayoutingType layoutType) {
+ if ((LayoutingType.Sizing | layoutType) != LayoutingType.None) {
+ if (!System.Threading.Monitor.TryEnter (linesMutex))
+ return false;
+ }
+ try {
+ bool result = base.UpdateLayout (layoutType);
+ return result;
+ } finally {
+ System.Threading.Monitor.Exit (linesMutex);
+ }
+ }
+ public override int measureRawSize(LayoutingType lt)
+ {
+ DbgLogger.StartEvent(DbgEvtType.GOMeasure, this, lt);
+
+ if ((bool)lines?.IsEmpty)
+ getLines ();
+
+ if (!textMeasureIsUpToDate) {
+ using (Context gr = new Context (IFace.surf)) {
+ setFontForContext (gr);
+ measureTextBounds (gr);
+ }
+ }
+ DbgLogger.EndEvent(DbgEvtType.GOMeasure);
+ return Margin * 2 + (lt == LayoutingType.Height ? cachedTextSize.Height : cachedTextSize.Width);
+ }
+
+ protected override void onDraw (Context gr)
+ {
+ base.onDraw (gr);
+
+ setFontForContext (gr);
+
+ if (!textMeasureIsUpToDate) {
+ lock (linesMutex)
+ measureTextBounds (gr);
+ }
+
+ if (ClipToClientRect) {
+ gr.Save ();
+ CairoHelpers.CairoRectangle (gr, ClientRectangle, CornerRadius);
+ gr.Clip ();
+ }
+
+ lock (linesMutex)
+ drawContent (gr);
+
+ if (ClipToClientRect)
+ gr.Restore ();
+ }
+ #endregion
+
+ #region Mouse handling
+ protected override void onFocused (object sender, EventArgs e)
+ {
+ base.onFocused (sender, e);
+
+ if (CurrentLoc == null) {
+ selectionStart = new CharLocation (0, 0);
+ CurrentLoc = new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length);
+ }
+
+ RegisterForRedraw ();
+
+ (IFace as CrowEditBase.CrowEditBase).CurrentEditor = this;
+ }
+ protected override void onUnfocused (object sender, EventArgs e)
+ {
+ base.onUnfocused (sender, e);
+ RegisterForRedraw ();
+ }
+ public override void onMouseEnter (object sender, MouseMoveEventArgs e) {
+ base.onMouseEnter (sender, e);
+ if (Focusable)
+ IFace.MouseCursor = MouseCursor.ibeam;
+ }
+ public override void onMouseMove (object sender, MouseMoveEventArgs e)
+ {
+ base.onMouseMove (sender, e);
+
+ updateHoverLocation (ScreenPointToLocal (e.Position));
+
+ if (HasFocus && IFace.IsDown (MouseButton.Left)) {
+ CurrentLoc = hoverLoc;
+ RegisterForRedraw ();
+ }
+ }
+ public override void onMouseDown (object sender, MouseButtonEventArgs e)
+ {
+ if (e.Button == Glfw.MouseButton.Left) {
+ targetColumn = -1;
+ if (HasFocus) {
+ if (!IFace.Shift)
+ selectionStart = hoverLoc;
+ else if (!selectionStart.HasValue)
+ selectionStart = CurrentLoc;
+ CurrentLoc = hoverLoc;
+ IFace.forceTextCursor = true;
+ RegisterForRedraw ();
+ e.Handled = true;
+ }
+ }
+ base.onMouseDown (sender, e);
+
+ //done at the end to set 'hasFocus' value after testing it
+ }
+ public override void onMouseUp (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseUp (sender, e);
+ if (e.Button != MouseButton.Left || !HasFocus || !selectionStart.HasValue)
+ return;
+ if (selectionStart.Value == CurrentLoc.Value)
+ selectionStart = null;
+ }
+ public override void onMouseDoubleClick (object sender, MouseButtonEventArgs e)
+ {
+ base.onMouseDoubleClick (sender, e);
+ if (e.Button != MouseButton.Left || !HasFocus)
+ return;
+
+ GotoWordStart ();
+ selectionStart = CurrentLoc;
+ GotoWordEnd ();
+ RegisterForRedraw ();
+ }
+ #endregion
+
+ #region Keyboard handling
+ public override void onKeyDown (object sender, KeyEventArgs e) {
+ Key key = e.Key;
+ TextSpan selection = Selection;
+ switch (key) {
+ case Key.Backspace:
+ if (selection.IsEmpty) {
+ if (selection.Start == 0)
+ return;
+ if (CurrentLoc.Value.Column == 0) {
+ int lbLength = lines[CurrentLoc.Value.Line - 1].LineBreakLength;
+ update (new TextChange (selection.Start - lbLength, lbLength, ""));
+ }else
+ update (new TextChange (selection.Start - 1, 1, ""));
+ } else
+ update (new TextChange (selection.Start, selection.Length, ""));
+ break;
+ case Key.Delete:
+ if (selection.IsEmpty) {
+ if (selection.Start == _text.Length)
+ return;
+ if (CurrentLoc.Value.Column >= lines[CurrentLoc.Value.Line].Length)
+ update (new TextChange (selection.Start, lines[CurrentLoc.Value.Line].LineBreakLength, ""));
+ else
+ update (new TextChange (selection.Start, 1, ""));
+ } else {
+ if (IFace.Shift)
+ IFace.Clipboard = SelectedText;
+ update (new TextChange (selection.Start, selection.Length, ""));
+ }
+ break;
+ case Key.Insert:
+ if (IFace.Shift)
+ Paste ();
+ else if (IFace.Ctrl)
+ Copy ();
+ break;
+ case Key.KeypadEnter:
+ case Key.Enter:
+ if (string.IsNullOrEmpty (LineBreak))
+ detectLineBreak ();
+ update (new TextChange (selection.Start, selection.Length, LineBreak));
+ break;
+ case Key.Escape:
+ selectionStart = null;
+ CurrentLoc = lines.GetLocation (selection.Start);
+ RegisterForRedraw ();
+ break;
+ case Key.Tab:
+ update (new TextChange (selection.Start, selection.Length, "\t"));
+ break;
+ case Key.PageUp:
+ checkShift ();
+ LineMove (-visibleLines);
+ RegisterForRedraw ();
+ break;
+ case Key.PageDown:
+ checkShift ();
+ LineMove (visibleLines);
+ RegisterForRedraw ();
+ break;
+ case Key.Home:
+ targetColumn = -1;
+ checkShift ();
+ if (IFace.Ctrl)
+ CurrentLoc = new CharLocation (0, 0);
+ else
+ CurrentLoc = new CharLocation (CurrentLoc.Value.Line, 0);
+ RegisterForRedraw ();
+ break;
+ case Key.End:
+ checkShift ();
+ int l = IFace.Ctrl ? lines.Count - 1 : CurrentLoc.Value.Line;
+ CurrentLoc = new CharLocation (l, lines[l].Length);
+ RegisterForRedraw ();
+ break;
+ case Key.Left:
+ checkShift ();
+ if (IFace.Ctrl)
+ GotoWordStart ();
+ else
+ MoveLeft ();
+ RegisterForRedraw ();
+ break;
+ case Key.Right:
+ checkShift ();
+ if (IFace.Ctrl)
+ GotoWordEnd ();
+ else
+ MoveRight ();
+ RegisterForRedraw ();
+ break;
+ case Key.Up:
+ checkShift ();
+ LineMove (-1);
+ RegisterForRedraw ();
+ break;
+ case Key.Down:
+ checkShift ();
+ LineMove (1);
+ RegisterForRedraw ();
+ break;
+ default:
+ base.onKeyDown (sender, e);
+ return;
+ }
+ autoAdjustScroll = true;
+ IFace.forceTextCursor = true;
+ e.Handled = true;
+ }
+ #endregion
+ #endregion
+
+
+ #region textBox
+ bool autoAdjustScroll = false;//if scrollXY is changed directly, dont try adjust scroll to cursor
+ internal RectangleD? computeTextCursor (Rectangle cursor) {
+ Rectangle cb = ClientRectangle;
+ cursor -= new Point (ScrollX, ScrollY);
+
+ if (autoAdjustScroll) {
+ autoAdjustScroll = false;
+ int goodMsrs = 0;
+ if (cursor.Right < 0)
+ ScrollX += cursor.Right;
+ else if (cursor.X > cb.Width)
+ ScrollX += cursor.X - cb.Width;
+ else
+ goodMsrs++;
+
+ if (cursor.Y < 0)
+ ScrollY += cursor.Y;
+ else if (cursor.Bottom > cb.Height)
+ ScrollY += cursor.Bottom - cb.Height;
+ else
+ goodMsrs++;
+
+ if (goodMsrs < 2)
+ return null;
+ } else if (cursor.Right < 0 || cursor.X > cb.Width || cursor.Y < 0 || cursor.Bottom > cb.Height)
+ return null;
+
+ return cursor;
+ }
+
+ void updateMaxScrolls (LayoutingType layout) {
+ Rectangle cb = ClientRectangle;
+ if (layout == LayoutingType.Width) {
+ MaxScrollX = cachedTextSize.Width - cb.Width;
+ NotifyValueChanged ("PageWidth", ClientRectangle.Width);
+ if (cachedTextSize.Width > 0)
+ NotifyValueChanged ("ChildWidthRatio", Math.Min (1.0, (double)cb.Width / cachedTextSize.Width));
+ } else if (layout == LayoutingType.Height) {
+ MaxScrollY = cachedTextSize.Height - cb.Height;
+ NotifyValueChanged ("PageHeight", ClientRectangle.Height);
+ if (cachedTextSize.Height > 0)
+ NotifyValueChanged ("ChildHeightRatio", Math.Min (1.0, (double)cb.Height / cachedTextSize.Height));
+ }
+ }
+ public virtual void Cut () {
+ TextSpan selection = Selection;
+ if (selection.IsEmpty)
+ return;
+ IFace.Clipboard = SelectedText;
+ update (new TextChange (selection.Start, selection.Length, ""));
+ }
+ public virtual void Copy () {
+ TextSpan selection = Selection;
+ if (selection.IsEmpty)
+ return;
+ IFace.Clipboard = SelectedText;
+ }
+ public virtual void Paste () {
+ TextSpan selection = Selection;
+ update (new TextChange (selection.Start, selection.Length, IFace.Clipboard));
+ }
+
+ #region Keyboard handling
+ public override void onKeyPress (object sender, KeyPressEventArgs e) {
+ base.onKeyPress (sender, e);
+
+ TextSpan selection = Selection;
+ update (new TextChange (selection.Start, selection.Length, e.KeyChar.ToString ()));
+
+ /*Insert (e.KeyChar.ToString());
+
+ SelRelease = -1;
+ SelBegin = new Point(CurrentColumn, SelBegin.Y);
+
+ RegisterForGraphicUpdate();*/
+ }
+ #endregion
+
+ protected void update (TextChange change) {
+ lock (linesMutex) {
+ ReadOnlySpan<char> src = _text.AsSpan ();
+ Span<char> tmp = stackalloc char[src.Length + (change.ChangedText.Length - change.Length)];
+ //Console.WriteLine ($"{Text.Length,-4} {change.Start,-4} {change.Length,-4} {change.ChangedText.Length,-4} tmp:{tmp.Length,-4}");
+ src.Slice (0, change.Start).CopyTo (tmp);
+ change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start));
+ src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length));
+
+ _text = tmp.ToString ();
+ lines.Update (change);
+ //lines.Update (_text);
+ selectionStart = null;
+
+ CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length);
+ textMeasureIsUpToDate = false;
+ IFace.forceTextCursor = true;
+ }
+
+ OnTextChanged (this, new TextChangeEventArgs (change));
+
+ RegisterForGraphicUpdate ();
+ }
+
+ #endregion
+
}
}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Crow;
+using CrowEditBase;
+using static CrowEditBase.CrowEditBase;
+
+namespace CrowEditBase
+{
+ public interface IFileNode
+ {
+ string FullPath { get; }
+ }
+}
--- /dev/null
+// Copyright (c) 2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Xml.Serialization;
+using System.ComponentModel;
+using System.Collections;
+using Crow.Cairo;
+
+namespace Crow
+{
+ public enum LogType {
+ Low,
+ Normal,
+ High,
+ Debug,
+ Warning,
+ Error,
+ Custom1,
+ Custom2,
+ Custom3,
+ }
+ public class LogEntry {
+ public LogType Type;
+ public string msg;
+ public LogEntry (LogType type, string message) {
+ Type = type;
+ msg = message;
+ }
+ public override string ToString() => msg;
+ }
+ public class LogViewerWidget : ScrollingObject
+ {
+ ObservableList<LogEntry> lines;
+ bool scrollOnOutput;
+ int visibleLines = 1;
+ FontExtents fe;
+
+ [DefaultValue(true)]
+ public virtual bool ScrollOnOutput {
+ get { return scrollOnOutput; }
+ set {
+ if (scrollOnOutput == value)
+ return;
+ scrollOnOutput = value;
+ NotifyValueChanged ("ScrollOnOutput", scrollOnOutput);
+
+ }
+ }
+ public virtual ObservableList<LogEntry> Lines {
+ get { return lines; }
+ set {
+ if (lines == value)
+ return;
+ if (lines != null) {
+ lines.ListAdd -= Lines_ListAdd;
+ lines.ListRemove -= Lines_ListRemove;
+ }
+ lines = value;
+ if (lines != null) {
+ lines.ListAdd += Lines_ListAdd;
+ lines.ListRemove += Lines_ListRemove;
+ }
+ NotifyValueChanged ("Lines", lines);
+ RegisterForGraphicUpdate ();
+ }
+ }
+
+ void Lines_ListAdd (object sender, ListChangedEventArg e)
+ {
+ // try
+ // {
+ MaxScrollY = lines.Count - visibleLines;
+ if (scrollOnOutput)
+ ScrollY = MaxScrollY;
+
+ // }
+ // catch (System.Exception ex)
+ // {
+ // Console.WriteLine ($"list add valueChange handler bug:{ex}");
+ // }
+ }
+
+ void Lines_ListRemove (object sender, ListChangedEventArg e)
+ {
+ MaxScrollY = lines.Count - visibleLines;
+ }
+
+
+ public override void OnLayoutChanges (LayoutingType layoutType)
+ {
+ base.OnLayoutChanges (layoutType);
+
+ if (layoutType == LayoutingType.Height) {
+ using (ImageSurface img = new ImageSurface (Format.Argb32, 10, 10)) {
+ using (Context gr = new Context (img)) {
+ //Cairo.FontFace cf = gr.GetContextFontFace ();
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+
+ fe = gr.FontExtents;
+ }
+ }
+ visibleLines = (int)Math.Floor ((double)ClientRectangle.Height / fe.Height);
+ MaxScrollY = lines == null ? 0 : lines.Count - visibleLines;
+ }
+ }
+ protected override void onDraw (Cairo.Context gr)
+ {
+ base.onDraw (gr);
+
+ if (lines == null)
+ return;
+
+ gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+ gr.SetFontSize (Font.Size);
+
+ Rectangle r = ClientRectangle;
+
+
+ double y = ClientRectangle.Y;
+ double x = ClientRectangle.X - ScrollX;
+
+ lock (lines) {
+ for (int i = 0; i < visibleLines; i++) {
+ if (i + ScrollY >= Lines.Count)
+ break;
+ //if ((lines [i + Scroll] as string).StartsWith ("error", StringComparison.OrdinalIgnoreCase)) {
+ // errorFill.SetAsSource (gr);
+ // gr.Rectangle (x, y, (double)r.Width, fe.Height);
+ // gr.Fill ();
+ // Foreground.SetAsSource (gr);
+ //}
+ LogEntry le = lines[i+ScrollY];
+ switch (le.Type) {
+ case LogType.Low:
+ gr.SetSource (Colors.DimGrey);
+ break;
+ case LogType.Normal:
+ gr.SetSource (Colors.Grey);
+ break;
+ case LogType.High:
+ gr.SetSource (Colors.White);
+ break;
+ case LogType.Debug:
+ gr.SetSource (Colors.Yellow);
+ break;
+ case LogType.Warning:
+ gr.SetSource (Colors.Orange);
+ break;
+ case LogType.Error:
+ gr.SetSource (Colors.Red);
+ break;
+ case LogType.Custom1:
+ gr.SetSource (Colors.Cyan);
+ break;
+ case LogType.Custom2:
+ gr.SetSource (Colors.Green);
+ break;
+ case LogType.Custom3:
+ gr.SetSource (Colors.LightPink);
+ break;
+ }
+ gr.MoveTo (x, y + fe.Ascent);
+ gr.ShowText (le.msg);
+ y += fe.Height;
+ gr.Fill ();
+ }
+ }
+ }
+
+ }
+}
+
public Assembly Load (AssemblyName assemblyName)
=> loadContext.LoadFromAssemblyName (assemblyName);
+
+ public bool TryGet (AssemblyName assemblyName, out Assembly assembly) {
+ assembly = loadContext.Assemblies.FirstOrDefault (a=>a.GetName().Name == assemblyName.Name);
+ return assembly != null;
+ }
public virtual bool IsLoaded {
get { return isLoaded; }
set {
}*/
public class PluginsLoadContext : AssemblyLoadContext {
public readonly Assembly MainAssembly;
- string pluginDirectory;
- public PluginsLoadContext (string pluginsDirectory)
- : base ($"CrowEditPluginsContext+{pluginsDirectory}", true) {
- this.pluginDirectory = pluginsDirectory;
- string pluginAssembly = Path.Combine (pluginsDirectory, $"{Path.GetFileName (pluginsDirectory)}.dll");
+ public readonly string Name;
+ readonly string fullPath;
+ public PluginsLoadContext (string pluginDirectory)
+ : base (Path.GetFileName (pluginDirectory), false) {
+ fullPath = pluginDirectory;
+ Name = Path.GetFileName (pluginDirectory);
+
+ string pluginAssembly = Path.Combine (fullPath, $"{Name}.dll");
MainAssembly = LoadFromAssemblyPath (pluginAssembly);
}
protected override Assembly Load(AssemblyName assemblyName) {
- string assemblyPath = Path.Combine (pluginDirectory, assemblyName.Name + ".dll");
+ string assemblyPath = Path.Combine (fullPath, assemblyName.Name + ".dll");
return File.Exists (assemblyPath) ? LoadFromAssemblyPath (assemblyPath) : null;
}
FullPath = fullPath;
}
public Command CMDLoad, CMDUnload, CMDReload;
- public CommandGroup Commands => new CommandGroup (
+ public virtual CommandGroup Commands => new CommandGroup (
CMDLoad, CMDUnload, CMDReload);
- protected virtual void initCommands () {
+ void initCommands () {
CMDLoad = new Command ("Load", Load, "#icons.reply.svg", false);
CMDUnload = new Command ("Unload", Unload, "#icons.share-arrow.svg", false);
CMDReload = new Command ("Reload", () => { Unload(); Load();}, "#icons.refresh.svg", false);
if (IFace.Shift) {
for (int l = lineStart; l <= lineEnd; l++) {
- if (Text[lines[l].Start] == '\t')
+ if (_text[lines[l].Start] == '\t')
update (new TextChange (lines[l].Start, 1, ""));
- else if (Char.IsWhiteSpace (Text[lines[l].Start])) {
+ else if (Char.IsWhiteSpace (_text[lines[l].Start])) {
int i = 1;
- while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i]))
+ while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (_text[i]))
i++;
update (new TextChange (lines[l].Start, i, ""));
}
-icon {
+Editor {
+ Background="White";
+ Foreground="Black";
+ MouseWheelSpeed = "20";
+ BubbleMouseEvent ="None";
+}
+
+icon {
Width="14";
Height="14";
}
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFrameworks>netcoreapp3.1</TargetFrameworks>
+ <TargetFrameworks>netcoreapp5</TargetFrameworks>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<EmbeddedResource Include="ui\**\*.*" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="../CERoslynPlugin\CERoslynPlugin.csproj" />
-
+ <ProjectReference Include="../CERoslynPlugin\CERoslynPlugin.csproj" />
</ItemGroup>
</Project>
initCommands ();
//resolve other plugins dependencies
- AssemblyLoadContext.GetLoadContext (Assembly.GetExecutingAssembly ()).Resolving += resolvePluginRefs;
+ //AssemblyLoadContext.GetLoadContext (Assembly.GetExecutingAssembly ()).Resolving += resolvePluginRefs;
if (CrowEditBase.CrowEditBase.App.TryGetWindow ("#CECrowDebugLog.ui.winLogGraph.crow", out Window win))
win.DataSource = this;
Events = events;
Widgets = widgets;
firstWidgetIndexToGet += widgets.Count;
- if (widgets.Count > 0 && firstWidgetIndexToGet != widgets.Last().InstanceIndex + 1)
- Debugger.Break ();
+ /*if (widgets.Count > 0 && firstWidgetIndexToGet != widgets.Last().InstanceIndex + 1)
+ Debugger.Break ();*/
}
}
void updateWidgetEvents (IList<DbgWidgetRecord> widgets, DbgEvent evt) {
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFrameworks>netcoreapp5</TargetFrameworks>
+ <EnableDefaultItems>false</EnableDefaultItems>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="src\**\*.cs" />
+ <EmbeddedResource Include="ui\**\*.*" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="../CERoslynPlugin\CERoslynPlugin.csproj" />
+ </ItemGroup>
+
+
+</Project>
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System.Diagnostics;
+
+namespace NetcoreDbgPlugin
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public class BreakPoint : CrowEditBase.BreakPoint
+ {
+ public BreakPoint(string fileFullPath, int line, bool isEnabled = true) : base (fileFullPath, line, isEnabled) {
+ }
+
+ public void Update (MITupple bkpt) {
+ Index = int.Parse (bkpt.GetAttributeValue("number"));
+ Type = bkpt.GetAttributeValue("type");
+ Disp = bkpt.GetAttributeValue("disp");
+ IsEnabled = bkpt.GetAttributeValue("enabled") == "y";
+ if (bkpt.TryGetAttributeValue("warning", out string warning))
+ Warning = warning;
+ else {
+ Warning = null;
+ Function = bkpt.GetAttributeValue("func");
+ //FileName = bkpt.GetAttributeValue("file");
+ FileFullPath = bkpt.GetAttributeValue("fullname")?.Replace("\\\\", "\\");
+ Line = int.Parse (bkpt.GetAttributeValue("line")) - 1;
+
+ /*if (project.TryGetProjectFileFromPath(FileFullName, out ProjectFileNode pf))
+ File = pf as CSProjectItem;*/
+
+ }
+ }
+
+ public override string ToString() => $"{Index}:{Type} {FileFullPath}:{Line} enabled:{IsEnabled}";
+ private string GetDebuggerDisplay() => ToString();
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+
+namespace NetcoreDbgPlugin
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public class CLRAddress
+ {
+ public string ModuleID;
+ public string MethodToken;
+ public long IlOffset;
+ public long NativeOffset;
+ public CLRAddress(MITupple clrAddress)
+ {
+ ModuleID = clrAddress.GetAttributeValue("module-id");
+ MethodToken = clrAddress.GetAttributeValue("method-token");
+ IlOffset = long.Parse(clrAddress.GetAttributeValue("il-offset"));
+ NativeOffset = long.Parse(clrAddress.GetAttributeValue("native-offset"));
+ }
+ public override string ToString() => $"Mod:{ModuleID} Meth:{MethodToken} IL:{IlOffset} Native:{NativeOffset}";
+ private string GetDebuggerDisplay() => ToString();
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+
+namespace NetcoreDbgPlugin
+{
+ [DebuggerDisplay("{Name}")]
+ public class MIObject
+ {
+ public string Name;
+ public MIObject(ReadOnlySpan<char> name)
+ {
+ Name = name.ToString();
+ }
+ }
+ [DebuggerDisplay("{Name}={Value}")]
+ public class MIAttribute : MIObject
+ {
+ public string Value;
+ public MIAttribute(ReadOnlySpan<char> name, ReadOnlySpan<char> value) : base(name)
+ {
+ Value = value.ToString();
+ }
+ public T GetValue<T>()
+ {
+ Type type = typeof(T);
+ if (type == typeof(string))
+ return (T)(object)Value;
+ if (type.IsEnum)
+ {
+ return (T)Enum.Parse(typeof(T), Value);
+ }
+ else
+ {
+ MethodInfo miParse = type.GetMethod("Parse", new Type[] { typeof(string) });
+ if (miParse != null)
+ return (T)miParse.Invoke(null, new object[] { Value });
+ }
+ return (T)Convert.ChangeType(Value, type);
+ }
+ }
+ [DebuggerDisplay("{Name} ({Attributes.Count})")]
+ public class MITupple : MIObject
+ {
+ public List<MIObject> Attributes = new List<MIObject>();
+ public MITupple(ReadOnlySpan<char> name) : base(name) { }
+
+ public MIObject this[string attributeName]
+ => Attributes.FirstOrDefault(a => a.Name == attributeName);
+ public MIObject this[int index]
+ => Attributes[index];
+
+ public string GetAttributeValue(string attributeName)
+ => Attributes.OfType<MIAttribute>().FirstOrDefault(a => a.Name == attributeName)?.Value;
+ public bool TryGetAttributeValue(string attributeName, out MIAttribute value)
+ {
+ value = Attributes.OfType<MIAttribute>().FirstOrDefault(a => a.Name == attributeName);
+ return value != null;
+ }
+ public bool TryGetAttributeValue(string attributeName, out string value)
+ {
+ value = Attributes.OfType<MIAttribute>().FirstOrDefault(a => a.Name == attributeName)?.Value;
+ return !string.IsNullOrEmpty (value);
+ }
+
+ public static MITupple Parse (ReadOnlySpan<char> data)
+ {
+ int tokStart = 0;
+ int curPos = 0;
+
+ Stack<MIObject> mistack = new Stack<MIObject>();
+ mistack.Push(new MITupple("Root"));
+ ReadOnlySpan<char> curName = null;
+ MITupple tup = null;
+
+ while (curPos < data.Length)
+ {
+ switch (data[curPos])
+ {
+ case '[':
+ mistack.Push(new MIList(curName));
+ curName = null;
+ break;
+ case '{':
+ mistack.Push(new MITupple(curName));
+ curName = null;
+ break;
+ case '}':
+ tup = mistack.Pop() as MITupple;
+ if (mistack.Peek() is MITupple mit)
+ mit.Attributes.Add(tup);
+ else
+ (mistack.Peek() as MIList).Items.Add(tup);
+ break;
+ case ']':
+ MIList list = mistack.Pop() as MIList;
+ (mistack.Peek() as MITupple).Attributes.Add(list);
+ break;
+ case ',':
+ curName = null;
+ break;
+ case '"':
+ tup = mistack.Peek() as MITupple;
+ tokStart = ++curPos;
+ while (curPos < data.Length && !(data[curPos] == '"' && data[curPos-1] != '\\'))
+ curPos++;
+
+ tup.Attributes.Add(new MIAttribute(curName, data.Slice(tokStart, curPos - tokStart)));
+ break;
+ case '=':
+ curName = data.Slice(tokStart, curPos - tokStart);
+ break;
+ default:
+ curPos++;
+ continue;
+ }
+ tokStart = ++curPos;
+ }
+ return mistack.Pop() as MITupple;
+ }
+ }
+ [DebuggerDisplay("{Name} ({Items.Count})")]
+ public class MIList : MIObject
+ {
+ public List<MITupple> Items = new List<MITupple>();
+ public MIList(ReadOnlySpan<char> name) : base(name)
+ {
+ }
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2013-2019 Bruyère Jean-Philippe <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
+using CrowEditBase;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using static CrowEditBase.CrowEditBase;
+using Crow;
+
+
+namespace NetcoreDbgPlugin
+{
+ public class NetcoreDbgService : Service {
+ public NetcoreDbgService () : base () {
+ }
+ public string NetcoredbgPath {
+ get => Configuration.Global.Get<string> ("NetcoredbgPath");
+ set {
+ if (value == NetcoredbgPath)
+ return;
+ Configuration.Global.Set ("NetcoredbgPath", value);
+ NotifyValueChanged (value);
+ }
+ }
+ NetcoredbgDebugger dbg;
+ public override void Start() {
+ if (CurrentState == Status.Running)
+ return;
+ dbg = new NetcoredbgDebugger ();
+ CurrentState = Status.Running;
+ }
+ public override void Stop()
+ {
+ if (CurrentState != Status.Running)
+ return;
+
+ dbg.Terminate ();
+ dbg = null;
+ CurrentState = Status.Stopped;
+ }
+ public override void Pause()
+ {
+ if (CurrentState != Status.Running)
+ return;
+
+ dbg.Terminate ();
+ CurrentState = Status.Paused;
+ }
+
+ public override string ConfigurationWindowPath => "#CENetcoreDbgPlugin.ui.winConfiguration.crow";
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using CrowEditBase;
+using Crow;
+using CERoslynPlugin;
+
+using static CrowEditBase.CrowEditBase;
+
+namespace NetcoreDbgPlugin
+{
+ public class NetcoredbgDebugger : Debugger
+ {
+ System.Diagnostics.Process procdbg;
+ public class Request {
+ public readonly string Command;
+ public Request (string command) {
+ Command = command;
+ }
+ public override string ToString() => Command;
+ }
+ public class Request<T> : Request {
+ public T RequestObject;
+ public Request (T obj, string command) : base (command){
+ RequestObject = obj;
+ }
+ }
+ Queue<Request> pendingRequest = new Queue<Request>();
+ MSBuildProject msbProject => Project as MSBuildProject;
+
+ public override Project Project
+ {
+ get => base.Project;
+ set
+ {
+ if (base.Project == value || !(value is MSBuildProject msbProj))
+ return;
+ base.Project = value;
+ }
+ }
+ void initDebugSession () {
+ if (CurrentState != Status.Init || msbProject == null)
+ return;
+
+ bool result = procdbg.Start();
+
+ procdbg.BeginOutputReadLine();
+
+ CreateNewRequest($"-file-exec-and-symbols {msbProject.OutputAssembly}");
+ CreateNewRequest($"-environment-cd {Path.GetDirectoryName(msbProject.OutputAssembly)}");
+
+ foreach (BreakPoint bp in BreakPoints)
+ InsertBreakPoint(bp);
+
+ CurrentState = Status.Starting;
+ }
+ #region CTOR
+ public NetcoredbgDebugger()
+ {
+ procdbg = new System.Diagnostics.Process();
+ procdbg.StartInfo.FileName = App.GetService<NetcoreDbgService>().NetcoredbgPath;
+ procdbg.StartInfo.Arguments = "--interpreter=mi";
+ procdbg.StartInfo.CreateNoWindow = true;
+ procdbg.StartInfo.RedirectStandardInput = true;
+ procdbg.StartInfo.RedirectStandardOutput = true;
+ procdbg.StartInfo.RedirectStandardError = true;
+ procdbg.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8;
+ //procdbg.StartInfo.StandardInputEncoding = System.Text.Encoding.UTF8;
+ procdbg.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
+
+ procdbg.EnableRaisingEvents = true;
+ procdbg.OutputDataReceived += Procdbg_OutputDataReceived;
+ procdbg.ErrorDataReceived += Procdbg_ErrorDataReceived;
+ procdbg.Exited += Procdbg_Exited;
+
+ BreakPoints.ListAdd += BreakPoints_ListAdd;
+ BreakPoints.ListRemove += BreakPoints_ListRemove;
+ }
+ #endregion
+
+ public void Terminate () {
+ if (CurrentState == Status.Running || CurrentState == Status.Stopped)
+ Stop ();
+ procdbg?.Dispose();
+ }
+ /*protected override void ResetCurrentExecutingLocation()
+ {
+ if (executingFile == null)
+ return;
+ executingFile.ExecutingLine = -1;
+ executingFile = null;
+ }*/
+ /// <summary>
+ /// send request on netcoredbg process stdin
+ /// </summary>
+ void sendRequest(Request request)
+ {
+ DebuggerLog.Add($"<- {request}");
+ procdbg.StandardInput.WriteLine(request);
+ }
+
+ /// <summary>
+ /// enqueue new request, send it if no other request is pending
+ /// </summary>
+ public void CreateNewRequest(string request)
+ {
+ lock (pendingRequest)
+ {
+ pendingRequest.Enqueue(new Request (request));
+ if (pendingRequest.Count == 1)
+ sendRequest(pendingRequest.Peek());
+ }
+ }
+ public void CreateNewRequest(Request request)
+ {
+ lock (pendingRequest)
+ {
+ pendingRequest.Enqueue(request);
+ if (pendingRequest.Count == 1)
+ sendRequest(pendingRequest.Peek());
+ }
+ }
+
+ #region Debugger abstract class implementation
+ public override void Start()
+ {
+ initDebugSession ();
+ CreateNewRequest($"-exec-run");
+ }
+ public override void Pause()
+ {
+ CreateNewRequest($"-exec-interrupt");
+ }
+ public override void Continue()
+ {
+ CreateNewRequest($"-exec-continue");
+ }
+ public override void Stop()
+ {
+ CreateNewRequest($"-exec-abort");
+ }
+
+ public override void StepIn()
+ {
+ CreateNewRequest($"-exec-step");
+ }
+ public override void StepOver()
+ {
+ CreateNewRequest($"-exec-next");
+ }
+ public override void StepOut()
+ {
+ CreateNewRequest($"-exec-finish");
+ }
+
+ public override void InsertBreakPoint(CrowEditBase.BreakPoint bp)
+ {
+ BreakPoint bk = bp as BreakPoint;
+ CreateNewRequest (new Request<BreakPoint> (bk, $"-break-insert {bk.FileFullPath}:{bk.Line + 1}"));
+ }
+ public override void DeleteBreakPoint(CrowEditBase.BreakPoint bp)
+ {
+ if (bp.Index < 0)
+ return;
+ CreateNewRequest($"-break-delete {bp.Index}");
+ }
+ protected override void onCurrentFrameChanged () {
+ if (CurrentFrame == null)
+ return;
+ tryGoTo((StackFrame)CurrentFrame);
+ updateWatches ();
+ }
+ protected override void onCurrentThreadChanged () {
+ if (CurrentThread == null)
+ return;
+ getStackFrames((ThreadInfo)CurrentThread);
+ updateWatches ();
+ }
+ #endregion
+
+ public void GetStackFrames(MIList list)
+ {
+
+ }
+
+ private void BreakPoints_ListRemove(object sender, ListChangedEventArg e)
+ {
+ if (CurrentState == Status.Init)
+ return;
+ DeleteBreakPoint((BreakPoint)e.Element);
+ }
+ private void BreakPoints_ListAdd(object sender, ListChangedEventArg e)
+ {
+ if (CurrentState == Status.Init)
+ return;
+ InsertBreakPoint((BreakPoint)e.Element);
+ }
+ private void Procdbg_Exited(object sender, EventArgs e)
+ {
+ DebuggerLog.Add("GDB process Terminated.");
+
+ CurrentState = Status.Init;
+ }
+
+ void getStackFrames(ThreadInfo thread = null)
+ {
+ if (thread == null)
+ CreateNewRequest($"-stack-list-frames");
+ else
+ CreateNewRequest($"-stack-list-frames --thread {thread.Id}");
+ }
+ void getVariables(ThreadInfo thread = null, int stackLevel = 0)
+ {
+ CreateNewRequest($"-stack-list-variables");
+ }
+ void updateWatches () {
+ foreach (Watch w in Watches)
+ w.UpdateValue ();
+ }
+
+ void tryGoTo(StackFrame frame)
+ {
+ /*if (string.IsNullOrEmpty(frame.FileFullName))
+ return;
+ executingLine = frame.Line - 1;
+ string strPath = frame.FileFullName;
+
+ if (project.TryGetProjectFileFromPath(strPath, out ProjectFileNode pf))
+ {
+ if (!pf.IsOpened)
+ pf.Open();
+ pf.IsSelected = true;
+
+ executingFile = pf as CSProjectItem;
+ executingFile.ExecutingLine = executingLine;
+ executingFile.CurrentLine = executingLine;
+ }
+ else
+ {
+ ResetCurrentExecutingLocation();
+ DebuggerLog.Add($"[ERROR]:current executing file ({strPath}) not found.");
+ }*/
+ }
+ bool hasPendingRequest {
+ get {
+ lock (pendingRequest) {
+ return pendingRequest.Count > 0;
+ }
+ }
+ }
+
+ void Procdbg_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) {
+ DebuggerLog.Add($"-> Error: {e.Data}");
+ }
+
+ void Procdbg_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) {
+ if (string.IsNullOrEmpty(e.Data))
+ return;
+
+ DebuggerLog.Add($"-> {e.Data}");
+
+ char firstChar = e.Data[0];
+ ReadOnlySpan<char> data = e.Data.AsSpan(1);
+
+ if (firstChar == '(')
+ return;
+
+ int tokEnd = data.IndexOf(',');
+
+ ReadOnlySpan<char> data_id = tokEnd < 0 ? data : data.Slice(0, tokEnd);
+ if (tokEnd >= 0)
+ data = data.Slice(tokEnd + 1);
+
+ MITupple obj = MITupple.Parse (data);
+
+ if (firstChar == '^')
+ {
+ Request request = null;
+ lock (pendingRequest)
+ {
+ if (pendingRequest.Count > 0)
+ {
+ request = pendingRequest.Dequeue();
+ if (pendingRequest.Count > 0)
+ sendRequest(pendingRequest.Peek());
+ }
+ }
+
+ if (data_id.SequenceEqual("running"))
+ {
+ CurrentState = Status.Running;
+ CurrentFrame = null;
+ CurrentThread = null;
+ Threads.Clear();
+ Frames.Clear();
+ }
+ else if (data_id.SequenceEqual("done"))
+ {
+ if (obj.Attributes.Count > 0)
+ {
+ if (obj[0].Name == "threads") {
+ MIList threads = obj[0] as MIList;
+ Threads.Clear();
+ foreach (MITupple t in threads.Items)
+ Threads.Add(new ThreadInfo(t));
+ } else if (obj[0].Name == "stack") {
+ MIList stack = obj[0] as MIList;
+ Frames.Clear();
+ foreach (MITupple f in stack.Items)
+ Frames.Add(new StackFrame(f));
+ } else if (request is Request<Watch> w) {
+ /*if (w.Command.StartsWith ("-var-delete")) {
+ Watches.Remove (w.RequestObject);
+ } else*/
+ if (w.Command.StartsWith("-var-list-children")) {
+ if (int.Parse (obj.GetAttributeValue ("numchild")) > 0) {
+ foreach (MITupple child in (obj["children"] as MIList).Items)
+ w.RequestObject.Children.Add(new Watch(this, child));
+ }
+ } else if (w.Command.StartsWith ("-var-evaluate"))
+ w.RequestObject.Value = obj.GetAttributeValue("value");
+ else
+ w.RequestObject.Update (obj);
+ } else if (request is Request<BreakPoint> bpReq) {
+ BreakPoint bp = bpReq.RequestObject;
+ bp.Update (obj["bkpt"] as MITupple);
+
+ } else
+ DebuggerLog.Add($"=> request result not handled: {request}");
+ }
+
+
+ }
+ else if (data_id.SequenceEqual("exit"))
+ {
+ DebuggerLog.Add($"=> exit request done: {request}");
+ CreateNewRequest($"-gdb-exit");
+ }
+ else
+ print_unknown_datas($"requested: {request} data:{e.Data}");
+
+ }
+ else if (firstChar == '*')
+ {
+ if (data_id.SequenceEqual("stopped"))
+ {
+ CurrentState = Status.Stopped;
+ string reason = obj.GetAttributeValue("reason");
+ if (reason == "exited")
+ {
+ CurrentState = Status.Ready;
+ DebuggerLog.Add($"Exited({obj.GetAttributeValue("exit-code")})");
+ //CreateNewRequest($"-gdb-exit");
+ }
+ else if (reason == "entry-point-hit" && !BreakOnStartup) {
+ Continue();
+ } else {
+ DebuggerLog.Add($"Stopped reason:{reason}");
+
+ StackFrame frame = new StackFrame(obj["frame"] as MITupple);
+ if (reason == "breakpoint-hit") {
+ BreakPoint bp = (BreakPoint)BreakPoints.FirstOrDefault (bk=>bk.Index == int.Parse (obj.GetAttributeValue ("bkptno")));
+ bp.UpdateLocation (frame);
+ }
+
+ tryGoTo(frame);
+
+ CreateNewRequest($"-thread-info");
+ getStackFrames();
+ getVariables();
+ updateWatches ();
+ }
+
+ } else
+ print_unknown_datas(e.Data);
+ } else if (firstChar == '=') {//EVENTS
+ if (data_id.SequenceEqual("message")) {
+ OutputLog.Add(obj.GetAttributeValue("text").ToString().Replace(@"\0", ""));
+ } else if (data_id.SequenceEqual("breakpoint-modified")) {
+ OutputLog.Add($"{e.Data}");
+ MITupple bkpt = obj["bkpt"] as MITupple;
+ BreakPoint bp = (BreakPoint)BreakPoints.FirstOrDefault (bk=>bk.Index == int.Parse (bkpt.GetAttributeValue("number")));
+ bp.Update (bkpt);
+ }
+ } else
+ print_unknown_datas(e.Data);
+ }
+
+ void print_unknown_datas(string data)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine(data);
+ Console.ResetColor();
+ }
+
+ public void WatchRequest(Watch w)
+ {
+ string strThread = CurrentThread == null ? "" : $"--thread {CurrentThread.Id}";
+ string strLevel = CurrentFrame == null ? "" : $"--frame {CurrentFrame.Level}";
+ CreateNewRequest (new Request<Watch> (w, $"-var-create {w.Name} {w.Expression} {strThread} {strLevel}"));
+ }
+ public void WatchChildrenRequest(Watch w)
+ {
+ string strThread = CurrentThread == null ? "" : $"--thread {CurrentThread.Id}";
+ string strLevel = CurrentFrame == null ? "" : $"--frame {CurrentFrame.Level}";
+ CreateNewRequest (new Request<Watch> (w, $"-var-list-children 1 {w.Name} {strThread} {strLevel}"));
+ }
+
+ public void OnValidateCommand(Object sender, ValidateEventArgs e)
+ {
+ CreateNewRequest(e.ValidatedText);
+ (sender as TextBox).Text = "";
+ }
+
+ public void OnValidateNewWatch(Object sender, ValidateEventArgs e)
+ {
+ Watch w = new Watch(this, e.ValidatedText);
+ Watches.Add(w);
+ WatchRequest(w);
+ (sender as TextBox).Text = "";
+ }
+
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+
+namespace NetcoreDbgPlugin
+{
+ [DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
+ public class StackFrame : CrowEditBase.StackFrame
+ {
+ public bool HasCLRAddress => ClrAddress != null;
+ public CLRAddress ClrAddress;
+
+ public StackFrame(MITupple frame)
+ {
+ if (frame.TryGetAttributeValue("level", out MIAttribute level))
+ this.Level = int.Parse(level.Value);
+ //File = frame.GetAttributeValue("file");
+ FileFullPath = frame.GetAttributeValue("fullname")?.Replace("\\\\", "\\");
+ int.TryParse(frame.GetAttributeValue("line"), out Line);
+ int.TryParse(frame.GetAttributeValue("col"), out Column);
+ int.TryParse(frame.GetAttributeValue("end-line"), out LineEnd);
+ int.TryParse(frame.GetAttributeValue("end-col"), out ColumnEnd);
+ Function = frame.GetAttributeValue("func");
+ Address = frame.GetAttributeValue("addr");
+ MITupple clrAddrs = frame["clr-addr"] as MITupple;
+ if (clrAddrs != null)
+ ClrAddress = new CLRAddress(clrAddrs);
+ }
+ public override string ToString() => $"{Level}:{FileFullPath}({Line},{Column} {Function})";
+ string GetDebuggerDisplay() => ToString();
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+
+namespace NetcoreDbgPlugin
+{
+ public class ThreadInfo : CrowEditBase.ThreadInfo
+ {
+ public ThreadInfo(MITupple frame)
+ {
+ Id = int.Parse(frame.GetAttributeValue("id"));
+ Name = frame.GetAttributeValue("name");
+ IsStopped = frame.GetAttributeValue("state") == "stopped";
+ }
+ }
+}
--- /dev/null
+// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+//using static Crow.Coding.NetcoredbgDebugger;
+using Crow;
+
+namespace NetcoreDbgPlugin
+{
+ public class Watch : CrowEditBase.Watch {
+ static int curId;
+
+ NetcoredbgDebugger dbg;
+
+ protected override void onExpand() {
+ if (HasChildren && Children.Count == 0)
+ dbg.WatchChildrenRequest (this);
+ }
+
+
+ public override void Create()
+ {
+ }
+ public override void Delete()
+ {
+ dbg.CreateNewRequest (new NetcoredbgDebugger.Request<Watch> (this, $"-var-delete {Name}"));
+ dbg.Watches.Remove (this);
+ }
+ public override void UpdateValue () {
+ string strThread = dbg.CurrentThread == null ? "" : $"--thread {dbg.CurrentThread.Id}";
+ string strLevel = dbg.CurrentFrame == null ? "" : $"--frame {dbg.CurrentFrame.Level}";
+ dbg.CreateNewRequest (new NetcoredbgDebugger.Request<Watch> (this, $"-var-evaluate-expression {Name} {strThread} {strLevel}"));
+ foreach (Watch w in Children)
+ w.UpdateValue ();
+ }
+ public Watch(NetcoredbgDebugger debugger, string expression)
+ {
+ dbg = debugger;
+ Name = $"watch_{curId++}";
+ Expression = expression;
+ }
+ public Watch(NetcoredbgDebugger debugger, MITupple variable)
+ {
+ dbg = debugger;
+ Update (variable);
+ }
+ public void Update (MITupple variable)
+ {
+ Name = variable.GetAttributeValue("name");
+ Expression = variable.GetAttributeValue("exp");
+ Value = variable.GetAttributeValue("value");
+ IsEditable = variable.GetAttributeValue("attributes") == "editable";
+ Type = variable.GetAttributeValue("type");
+ NumChild = int.Parse(variable.GetAttributeValue("numchild"));
+ ThreadId = int.Parse(variable.GetAttributeValue("thread-id"));
+ NotifyValueChanged ("HasChildren", HasChildren);
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0"?>
+<DockWindow Caption="Roslyn plugin configuration" Width="300" Height="400" Resizable="false">
+ <VerticalStack RootDataLevel="true" Margin="5">
+ <HorizontalStack Height="Fit">
+ <Label Width="200" Text="DotNet SDK folder"/>
+ <TextBox Width="Stretched" Text="{²SDKFolder}"/>
+ <Button Command="{CMDOptions_SelectSDKFolder}" MinimumSize="0,0"/>
+ </HorizontalStack>
+ <HorizontalStack Height="Fit">
+ <Label Width="200" Text="MSBuild root folder"/>
+ <TextBox Width="Stretched" Text="{²MSBuildRoot}"/>
+ <Button Command="{CMDOptions_SelectMSBuildRoot}" MinimumSize="0,0"/>
+ </HorizontalStack>
+ <HorizontalStack Height="Fit">
+ <Label Width="200" Text="Netcoredbg path"/>
+ <TextBox Width="Stretched" Text="{²NetcoredbgPath}"/>
+ <Button Command="{CMDOptions_SelectNetcoredbgPath}" MinimumSize="0,0"/>
+ </HorizontalStack>
+ </VerticalStack>
+</DockWindow>
+
+
--- /dev/null
+<?xml version="1.0"?>
+
+
+
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFrameworks>netcoreapp3.1</TargetFrameworks>
- <EnableDefaultItems>false</EnableDefaultItems>
- <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+ <TargetFrameworks>netcoreapp5</TargetFrameworks>
+ <EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="src\**\*.cs" />
+ <EmbeddedResource Include="ui\**\*.*" />
<EmbeddedResource Include="default.conf" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
- <PackageReference Include="Microsoft.Build" Version="16.8.*">
+ <PackageReference Include="Microsoft.Build" Version="15.1.*">
<PrivateAssets>all</PrivateAssets>
<Private>true</Private>
+ <Visible>False</Visible>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.Build.Framework" Version="16.8.*">
+ <PackageReference Include="Microsoft.Build.Framework" Version="15.1.*">
<PrivateAssets>all</PrivateAssets>
+ <Visible>False</Visible>
<Private>true</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.Build.Tasks.Core" Version="16.8.*">
+ <PackageReference Include="Microsoft.Build.Tasks.Core" Version="15.1.*">
<PrivateAssets>all</PrivateAssets>
+ <Visible>False</Visible>
<Private>true</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
- <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.8.*">
+ <PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.1.*">
<PrivateAssets>all</PrivateAssets>
+ <Visible>False</Visible>
<Private>true</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.8.0-5.final" >
- <IncludeAssets>all</IncludeAssets>
- </PackageReference>
- <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0-5.final" >
- <IncludeAssets>all</IncludeAssets>
- </PackageReference>
- <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.8.0-5.final" >
- <IncludeAssets>all</IncludeAssets>
- </PackageReference>
- <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.8.0-5.final" >
+ <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.7.*" >
<IncludeAssets>all</IncludeAssets>
+ <PrivateAssets>all</PrivateAssets>
+ <ExcludeAssets>runtime</ExcludeAssets>
+ <Visible>False</Visible>
</PackageReference>
- <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="3.8.0-5.final" >
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.7.*" >
<IncludeAssets>all</IncludeAssets>
+ <PrivateAssets>all</PrivateAssets>
+ <ExcludeAssets>runtime</ExcludeAssets>
+ <Visible>False</Visible>
</PackageReference>
<!--<PackageReference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel" />-->
</ItemGroup>
--- /dev/null
+using System.Reflection;
+// Copyright (c) 2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+using System.Linq;
+using Crow;
+using System.Text.RegularExpressions;
+using Microsoft.Build.Framework;
+
+using static CrowEditBase.CrowEditBase;
+
+namespace CERoslynPlugin
+{
+ internal class CELogger : ILogger
+ {
+ IEventSource eventSource;
+ LoggerVerbosity verbosity;
+
+ public LoggerVerbosity Verbosity {
+ get => verbosity;
+ set {
+ if (verbosity == value)
+ return;
+ if (eventSource != null)
+ unregisterHandles ();
+ verbosity = value;
+ if (eventSource != null)
+ registerHandles ();
+ }
+ }
+ public string Parameters { get; set; }
+
+ public CELogger (LoggerVerbosity verbosity = LoggerVerbosity.Detailed)
+ {
+ this.verbosity = verbosity;
+ }
+ public void Initialize (IEventSource eventSource) {
+ this.eventSource = eventSource;
+ registerHandles ();
+ }
+
+
+ void registerHandles () {
+ eventSource.WarningRaised += EventSource_WarningRaised;
+ eventSource.ErrorRaised += EventSource_ErrorRaised;
+ eventSource.BuildStarted += EventSource_Progress_BuildStarted;
+ eventSource.BuildFinished += EventSource_Progress_BuildFinished;
+
+ switch (Verbosity) {
+ case LoggerVerbosity.Minimal:
+ eventSource.MessageRaised += EventSource_MessageRaised_Minimal;
+ break;
+ case LoggerVerbosity.Normal:
+ eventSource.MessageRaised += EventSource_MessageRaised_Normal;
+ eventSource.ProjectStarted += EventSource_ProjectStarted;
+ eventSource.ProjectFinished += EventSource_ProjectFinished;
+ break;
+ case LoggerVerbosity.Detailed:
+ eventSource.MessageRaised += EventSource_MessageRaised_All;
+ eventSource.ProjectStarted += EventSource_ProjectStarted;
+ eventSource.ProjectFinished += EventSource_ProjectFinished;
+ eventSource.TargetStarted += EventSource_TargetStarted;
+ eventSource.TargetFinished += EventSource_TargetFinished;
+ eventSource.TaskStarted += EventSource_TaskStarted;
+ eventSource.TaskFinished += EventSource_TaskFinished;
+ break;
+ case LoggerVerbosity.Diagnostic:
+ eventSource.AnyEventRaised += EventSource_AnyEventRaised;
+ break;
+ }
+ }
+
+ void unregisterHandles () {
+ eventSource.WarningRaised -= EventSource_WarningRaised;
+ eventSource.ErrorRaised -= EventSource_ErrorRaised;
+
+
+ switch (Verbosity) {
+ case LoggerVerbosity.Minimal:
+ eventSource.MessageRaised -= EventSource_MessageRaised_Minimal;
+ break;
+ case LoggerVerbosity.Normal:
+ eventSource.MessageRaised -= EventSource_MessageRaised_Normal;
+ eventSource.ProjectStarted -= EventSource_ProjectStarted;
+ eventSource.ProjectFinished -= EventSource_ProjectFinished;
+ break;
+ case LoggerVerbosity.Detailed:
+ eventSource.MessageRaised -= EventSource_MessageRaised_All;
+ eventSource.ProjectStarted -= EventSource_ProjectStarted;
+ eventSource.ProjectFinished -= EventSource_ProjectFinished;
+ eventSource.TargetStarted -= EventSource_TargetStarted;
+ eventSource.TargetFinished -= EventSource_TargetFinished;
+ eventSource.TaskStarted -= EventSource_TaskStarted;
+ eventSource.TaskFinished -= EventSource_TaskFinished;
+ break;
+ case LoggerVerbosity.Diagnostic:
+ eventSource.AnyEventRaised -= EventSource_AnyEventRaised;
+ break;
+ }
+
+ }
+ void log (LogType type, string message) {
+ string[] lines = Regex.Split (message, "\r\n|\r|\n");//|\r|\n|\\\\n");
+ for (int i=0; i<lines.Length;i++)
+ App.Log (type, lines[i]);
+ }
+ void EventSource_Progress_BuildStarted (object sender, BuildStartedEventArgs e)
+ {
+ App.ResetLog ();
+ log (LogType.High, "Build starting.");
+ }
+ void EventSource_Progress_BuildFinished (object sender, BuildFinishedEventArgs e)
+ {
+ log (LogType.High, "Build Finished.");
+ //ide.CurrentSolution.RaiseDiagnosticsValueChanged();
+ }
+
+ private void EventSource_TaskFinished (object sender, TaskFinishedEventArgs e) {
+ log (LogType.Custom1, e.Message);
+ }
+
+ private void EventSource_TaskStarted (object sender, TaskStartedEventArgs e) {
+ log (LogType.Custom1, e.Message);
+ }
+
+ private void EventSource_TargetFinished (object sender, TargetFinishedEventArgs e) {
+ log (LogType.Custom2, e.Message);
+ }
+
+ private void EventSource_TargetStarted (object sender, TargetStartedEventArgs e) {
+ log (LogType.Custom2, e.Message);
+ }
+ private void EventSource_MessageRaised (object sender, BuildMessageEventArgs e) {
+ log (LogType.Normal, e.Message);
+ }
+ private void EventSource_AnyEventRaised (object sender, BuildEventArgs e) {
+ log (LogType.Normal, e.Message);
+ }
+
+ private void EventSource_MessageRaised_Minimal (object sender, BuildMessageEventArgs e) {
+ if (e.Importance == MessageImportance.High)
+ log (LogType.High, e.Message);
+ }
+ private void EventSource_MessageRaised_Normal (object sender, BuildMessageEventArgs e) {
+ if (e.Importance == MessageImportance.Normal)
+ log (LogType.Normal, e.Message);
+ else if(e.Importance == MessageImportance.High)
+ log (LogType.High, e.Message);
+ }
+ private void EventSource_MessageRaised_All (object sender, BuildMessageEventArgs e) {
+ if (e.Importance == MessageImportance.Low)
+ log (LogType.Low, e.Message);
+ else if(e.Importance == MessageImportance.Normal)
+ log (LogType.Normal, e.Message);
+ else if(e.Importance == MessageImportance.High)
+ log (LogType.High, e.Message);
+ }
+ void EventSource_ProjectStarted (object sender, ProjectStartedEventArgs e)
+ {
+ log (LogType.High, e.Message);
+ }
+ void EventSource_ProjectFinished (object sender, ProjectFinishedEventArgs e)
+ {
+ log (LogType.High, e.Message);
+ }
+ void EventSource_ErrorRaised (object sender, BuildErrorEventArgs e)
+ {
+ log (LogType.Error, e.Message);
+ }
+ private void EventSource_WarningRaised (object sender, BuildWarningEventArgs e) {
+ log (LogType.Warning, e.Message);
+ }
+
+ public void Shutdown ()
+ {
+ if (eventSource != null)
+ unregisterHandles ();
+ }
+ }
+}
namespace CERoslynPlugin
{
- public class ConsoleLogger : ILogger
+ internal class ConsoleLogger : ILogger
{
IEventSource eventSource;
LoggerVerbosity verbosity;
}
public string Parameters { get; set; }
- public ConsoleLogger (LoggerVerbosity verbosity = LoggerVerbosity.Diagnostic)
+ public ConsoleLogger (LoggerVerbosity verbosity = LoggerVerbosity.Normal)
{
this.verbosity = verbosity;
}
}
void EventSource_ErrorRaised (object sender, BuildErrorEventArgs e)
{
+ Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine (e.Message);
+ Console.ResetColor();
}
private void EventSource_WarningRaised (object sender, BuildWarningEventArgs e) {
+ Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine (e.Message);
+ Console.ResetColor();
}
public void Shutdown ()
using Crow;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Collections.Generic;
using Microsoft.Build.Construction;
using static CrowEditBase.CrowEditBase;
using Project = CrowEditBase.Project;
+using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace CERoslynPlugin
SolutionProject solutionProject;
Microsoft.Build.Evaluation.Project project;
CSharpCompilationOptions compileOptions;
- public CSharpParseOptions parseOptions;
-
- public override string Name => projectInSolution.ProjectName;
+ CSharpParseOptions parseOptions;
+
public string RootDir => project.DirectoryPath;
- public MSBuildProject (SolutionProject solution, ProjectInSolution projectInSolution) : base (projectInSolution.AbsolutePath) {
+ static string[] defaultTargets = { "Restore", "Build", "Rebuild", "Pack", "Publish"};
+
+ internal MSBuildProject (SolutionProject solution, ProjectInSolution projectInSolution) : base (projectInSolution.AbsolutePath) {
this.projectInSolution = projectInSolution;
this.solutionProject = solution;
Load ();
+
+ commands = new CommandGroup (CMDLoad, CMDUnload, CMDReload);
+ foreach (string target in defaultTargets)
+ Commands.Add (new Crow.Command (target, () => Build (target)));
}
+ CommandGroup commands;
+
+ public override CommandGroup Commands => commands;
+
public override void Load () {
if (IsLoaded)
return;
try
{
- ProjectRootElement projectRootElt = ProjectRootElement.Open (projectInSolution.AbsolutePath);
- project = new Microsoft.Build.Evaluation.Project (projectInSolution.AbsolutePath, null, null, solutionProject.projectCollection);
+ using (var ctx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext (this.GetType().Assembly).EnterContextualReflection()) {
+ ProjectRootElement projectRootElt = ProjectRootElement.Open (projectInSolution.AbsolutePath);
+ project = new Microsoft.Build.Evaluation.Project (projectInSolution.AbsolutePath, null, null, solutionProject.projectCollection);
- ProjectProperty msbuildProjExtPath = project.GetProperty ("MSBuildProjectExtensionsPath");
- ProjectProperty msbuildProjFile = project.GetProperty ("MSBuildProjectFile");
+ ProjectProperty msbuildProjExtPath = project.GetProperty ("MSBuildProjectExtensionsPath");
+ ProjectProperty msbuildProjFile = project.GetProperty ("MSBuildProjectFile");
- string[] props = { "EnableDefaultItems", "EnableDefaultCompileItems", "EnableDefaultNoneItems", "EnableDefaultEmbeddedResourceItems" };
+ string[] props = { "EnableDefaultItems", "EnableDefaultCompileItems", "EnableDefaultNoneItems", "EnableDefaultEmbeddedResourceItems" };
- foreach (string pr in props) {
- ProjectProperty pp = project.AllEvaluatedProperties.Where (ep => ep.Name == pr).FirstOrDefault ();
- if (pp == null)
- project.SetProperty (pr, "true");
- }
+ foreach (string pr in props) {
+ ProjectProperty pp = project.AllEvaluatedProperties.Where (ep => ep.Name == pr).FirstOrDefault ();
+ if (pp == null)
+ project.SetProperty (pr, "true");
+ }
- project.ReevaluateIfNecessary ();
-
- parseOptions = CSharpParseOptions.Default;
+ project.ReevaluateIfNecessary ();
+
+ parseOptions = CSharpParseOptions.Default;
- ProjectProperty langVersion = project.GetProperty ("LangVersion");
- if (langVersion != null && Enum.TryParse<LanguageVersion> (langVersion.EvaluatedValue, out LanguageVersion lv))
- parseOptions = parseOptions.WithLanguageVersion (lv);
- else
- parseOptions = parseOptions.WithLanguageVersion (LanguageVersion.Default);
+ ProjectProperty langVersion = project.GetProperty ("LangVersion");
+ if (langVersion != null && Enum.TryParse<LanguageVersion> (langVersion.EvaluatedValue, out LanguageVersion lv))
+ parseOptions = parseOptions.WithLanguageVersion (lv);
+ else
+ parseOptions = parseOptions.WithLanguageVersion (LanguageVersion.Default);
- ProjectProperty constants = project.GetProperty ("DefineConstants");
- if (constants != null)
- parseOptions = parseOptions.WithPreprocessorSymbols (constants.EvaluatedValue.Split (';'));
+ ProjectProperty constants = project.GetProperty ("DefineConstants");
+ if (constants != null)
+ parseOptions = parseOptions.WithPreprocessorSymbols (constants.EvaluatedValue.Split (';'));
- populateTreeNodes ();
+ populateTreeNodes ();
+ }
IsLoaded = true;
}
public void Build () => Build ("Build");
public void Build (params string[] targets)
{
- ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild (project);
- BuildRequestData request = new BuildRequestData (pi, targets,null,BuildRequestDataFlags.ProvideProjectStateAfterBuild);
- BuildResult result = BuildManager.DefaultBuildManager.Build (solutionProject.buildParams, request);
+ //using (var ctx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext (this.GetType().Assembly).EnterContextualReflection()) {
+ ProjectInstance pi = BuildManager.DefaultBuildManager.GetProjectInstanceForBuild (project);
+ BuildRequestData request = new BuildRequestData (pi, targets,null,BuildRequestDataFlags.ProvideProjectStateAfterBuild);
+ BuildResult result = BuildManager.DefaultBuildManager.Build (solutionProject.buildParams, request);
+ //}
}
TreeNode rootNode;
NotifyValueChanged ("Children", Children);
}
}
- public IList<TreeNode> Children => rootNode.Childs;
+ public IList<TreeNode> Children => rootNode?.Childs;
public override string Icon {
get {
switch (Path.GetExtension (FullPath)) {
break;
default:
- Console.ForegroundColor = ConsoleColor.Red;
+ /*Console.ForegroundColor = ConsoleColor.Grey;
Console.WriteLine ($"Unhandled Item Type: {pn.ItemType} {pn.EvaluatedInclude}");
- Console.ResetColor ();
+ Console.ResetColor ();*/
break;
}
}
item.Parent = this;
}*/
}
+
+ public override string Name => project == null ? projectInSolution.ProjectName : project.GetProperty ("MSBuildProjectName").EvaluatedValue;
+ public string ToolsVersion => project.ToolsVersion;
+ public string DefaultTargets => project.Xml.DefaultTargets;
+ public ICollection<ProjectProperty> Properties => project.Properties;
+ public ICollection<ProjectProperty> PropertiesSorted => project.Properties.OrderBy(p=>p.Name).ToList();
+ public string AssemblyName => project.GetProperty ("AssemblyName").EvaluatedValue;
+ public string OutputPath => project.GetProperty ("OutputPath").EvaluatedValue;
+ public string IntermediateOutputPath => project.GetProperty ("IntermediateOutputPath").EvaluatedValue;
+ public string OutputType => project.GetProperty ("OutputType").EvaluatedValue;
+ public string OutputAssembly =>
+ Path.Combine (project.GetPropertyValue ("OutputPath"), project.GetPropertyValue ("TargetFrameworks"), AssemblyName + AssemblyExtension);
+ public string AssemblyExtension => RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ? ".exe" : "";
+ public OutputKind OutputKind {
+ get {
+ switch (OutputType) {
+ case "Library":
+ return OutputKind.DynamicallyLinkedLibrary;
+ case "Exe":
+ return OutputKind.ConsoleApplication;
+ case "WinExe":
+ return OutputKind.WindowsApplication;
+ default:
+ return OutputKind.ConsoleApplication;
+ }
+ }
+ }
+ public string RootNamespace => project.GetProperty ("RootNamespace").EvaluatedValue;
+ public bool AllowUnsafeBlocks => bool.Parse (project.GetProperty ("AllowUnsafeBlocks").EvaluatedValue);
+ public bool NoStdLib => bool.Parse (project.GetProperty ("NoStdLib").EvaluatedValue);
+ public bool TreatWarningsAsErrors => bool.Parse (project.GetProperty ("TreatWarningsAsErrors").EvaluatedValue);
+ public bool SignAssembly => bool.Parse (project.GetProperty ("SignAssembly").EvaluatedValue);
+ public string TargetFrameworkVersion => project.GetProperty ("TargetFrameworkVersion").EvaluatedValue;
+ public string Description => project.GetProperty ("Description").EvaluatedValue;
+ public string StartupObject => project.GetProperty ("StartupObject").EvaluatedValue;
+ public bool DebugSymbols => bool.Parse (project.GetProperty ("DebugSymbols").EvaluatedValue);
+ public int WarningLevel => int.Parse (project.GetProperty ("WarningLevel").EvaluatedValue);
+
+
}
}
\ No newline at end of file
Always,
PreserveNewest
}*/
- public class ProjectItemNode : TreeNode
+ public class ProjectItemNode : TreeNode, IFileNode
{
ProjectItem projectItem;
}
}
}
+ public string FullPath =>
+ NodeType == NodeType.EmbeddedResource || NodeType == NodeType.None || NodeType == NodeType.Compile ?
+ Path.Combine (GetRoot<ProjectNode>().Project.RootDir, projectItem.EvaluatedInclude) : null;
+
public override CommandGroup Commands {
get {
switch (NodeType) {
case NodeType.Compile:
return new CommandGroup (
new Command ("Open", () => {
- App.OpenFile (Path.Combine (GetRoot<ProjectNode>().Project.RootDir, projectItem.EvaluatedInclude));
+ App.OpenFile (FullPath);
})
);
default:
namespace CERoslynPlugin
{
public class RoslynService : Service {
+
public RoslynService () : base () {
configureDefaultSDKPathes ();
//TODO static init to prevent rebinding on Service multiple instantiation
AssemblyLoadContext pluginCtx = AssemblyLoadContext.GetLoadContext (Assembly.GetExecutingAssembly());
pluginCtx.Resolving += msbuildResolve;
+
+ foreach (string dll in Directory.GetFiles (MSBuildRoot, "*.dll"))
+ pluginCtx.LoadFromAssemblyPath (dll);
+ string capath = Path.Combine (MSBuildRoot, "Roslyn", "bincore");
+ foreach (string dll in Directory.GetFiles (capath, "*.dll"))
+ pluginCtx.LoadFromAssemblyPath (dll);
}
Assembly msbuildResolve (AssemblyLoadContext context, AssemblyName assemblyName) {
string assemblyPath = Path.Combine (MSBuildRoot, assemblyName.Name + ".dll");
- return File.Exists (assemblyPath) ? Assembly.LoadFrom (assemblyPath) : null;
+ //return File.Exists (assemblyPath) ? context.LoadFromAssemblyPath (assemblyPath) : null;
+ if (!File.Exists (assemblyPath))
+ return null;
+ Assembly a = context.LoadFromAssemblyPath (assemblyPath);
+ Console.WriteLine ($"[MSBuildResolve]{a},{a.CodeBase}");
+ return a;
}
public override void Start() {
-
+
+ Environment.SetEnvironmentVariable ("MSBUILD_EXE_PATH", Path.Combine (MSBuildRoot, "MSBuild.dll"));
+ Environment.SetEnvironmentVariable ("MSBuildSDKsPath", Path.Combine (MSBuildRoot, "Sdks"));
+
+ if (Environment.OSVersion.Platform == PlatformID.Unix)
+ Environment.SetEnvironmentVariable ("FrameworkPathOverride", "/usr/lib/mono/4.5/");
CurrentState = Status.Running;
SolutionFile solutionFile;
- public ProjectCollection projectCollection { get; private set; }
- public BuildParameters buildParams { get; private set; }
+ internal ProjectCollection projectCollection { get; private set; }
+ internal BuildParameters buildParams { get; private set; }
public Configuration UserConfig { get; private set; }
public IEnumerable<string> Configurations => solutionFile.SolutionConfigurations.Select (sc => sc.ConfigurationName).Distinct ().ToList ();
public IEnumerable<string> Platforms => solutionFile.SolutionConfigurations.Select (sc => sc.PlatformName).Distinct ().ToList ();
public override void Load () {
projectCollection = new ProjectCollection (
- null,
- new ILogger [] { new ConsoleLogger () },
+ null,
+ new ILogger [] { new CELogger () },
ToolsetDefinitionLocations.Default
);
ActiveConfiguration = solutionFile.GetDefaultConfigurationName ();
ActivePlatform = solutionFile.GetDefaultPlatformName ();
+ projectCollection.SetGlobalProperty ("RestoreConfigFile", Path.Combine (
+ Path.Combine (
+ Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".nuget"), "NuGet"),
+ "NuGet.Config"));
+
projectCollection.SetGlobalProperty ("SolutionDir", Path.GetDirectoryName (FullPath) + Path.DirectorySeparatorChar);
projectCollection.SetGlobalProperty ("DefaultItemExcludes", "obj/**/*;bin/**/*");
//ide.projectCollection.HostServices
buildParams = new BuildParameters (projectCollection) {
Loggers = projectCollection.Loggers,
- LogInitialPropertiesAndItems = true,
- LogTaskInputs = true,
- /*UseSynchronousLogging = true*/
+ LogInitialPropertiesAndItems = false,
+ LogTaskInputs = false,
+ UseSynchronousLogging = true
};
//projectCollection.IsBuildEnabled = false;
<?xml version="1.0"?>
-<DockWindow Caption="Roslyn plugin configuration" Width="300" Height="400" Resizable="false">
+<DockWindow Caption="Roslyn plugin configuration" Width="80%" Height="400" Resizable="false">
<VerticalStack RootDataLevel="true" Margin="5">
<HorizontalStack Height="Fit">
<Label Width="200" Text="DotNet SDK folder"/>
}
Console.WriteLine($"[UNRESOLVE] {assembly} {libraryName}");
return IntPtr.Zero;
+ }
+
+ static Assembly last_chance_resolve (System.Runtime.Loader.AssemblyLoadContext context, AssemblyName assemblyName)
+ {
+ foreach (Plugin plugin in App.Plugins) {
+ if (plugin.TryGet (assemblyName, out Assembly assembly))
+ return assembly;
+ }
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine($"[UNRESOLVE] {assemblyName}");
+ Console.ResetColor();
+ return null;
}
static CrowEdit()
{
- System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged;
+ System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).ResolvingUnmanagedDll += resolveUnmanaged;
+ System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Resolving += last_chance_resolve;
}
#endif
static void Main ()
base.OnInitialized ();
loadPlugins ();
+ reopenLastProjectList ();
SetWindowIcon ("#Crow.Icons.crow.png");
reloadWinConfigs ();
reopenLastDocumentList ();
- reopenLastProjectList ();
}
public override void Terminate()
{
new Command("Explorer", (sender) => loadWindowWithThisDataSource (sender, "#CrowEdit.ui.windows.winFileExplorer.crow")),
new Command("Editors", (sender) => loadWindowWithThisDataSource (sender, "#CrowEdit.ui.windows.winEditor.crow")),
new Command("Projects", (sender) => loadWindowWithThisDataSource (sender, "#CrowEdit.ui.windows.winProjects.crow")),
+ new Command("Logs", (sender) => loadWindowWithThisDataSource (sender, "#CrowEdit.ui.windows.winLogs.crow")),
new Command("Crow Preview", (sender) => loadWindowWithThisDataSource (sender, "#CECrowDebugLog.ui.winCrowPreview.crow")),
new Command("Services", (sender) => loadWindowWithThisDataSource (sender, "#CrowEdit.ui.windows.winServices.crow")),
new Command("Plugins", (sender) => loadWindowWithThisDataSource (sender, "#CrowEdit.ui.windows.winPlugins.crow"))
if (doc != null)
CurrentDocument = doc;
}
+ void tv_projects_SelectedItemChanged (object sender, SelectionChangeEventArgs e) {
+ if (e.NewValue is IFileNode fi) {
+ if (string.IsNullOrEmpty (fi.FullPath) || ! File.Exists (fi.FullPath))
+ return;
+ if (TryGetDefaultTypeForExtension (Path.GetExtension (fi.FullPath), out Type clientType)) {
+ if (typeof(Document).IsAssignableFrom (clientType)) {
+ if (OpenedDocuments.FirstOrDefault (d => d.FullPath == fi.FullPath) is Document doc)
+ CurrentDocument = doc;
+ /*} else if (typeof(Service).IsAssignableFrom (clientType))
+ doc = GetService (clientType)?.OpenDocument (CurrentFilePath);*/
+ } else if (typeof(Project).IsAssignableFrom (clientType)) {
+ if (Projects.FirstOrDefault (p=>p.FullPath == fi.FullPath) is Project prj)
+ CurrentProject = prj;
+ }
+ }
+ }
+ }
+
void saveOpenedDocumentList () {
if (OpenedDocuments.Count == 0)
Configuration.Global.Set ("OpenedItems", "");
+++ /dev/null
-// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.IO;
-using Crow;
-
-namespace CrowEditBase
-{
- public class DirectoryNode : TreeNode
- {
- DirectoryInfo info;
- public DirectoryInfo Info => info;
- public DirectoryNode (DirectoryInfo info, TreeNode parent = null) : base (parent) {
- this.info = info;
- }
- public override string Name => info.Name;
-
- public override CommandGroup Commands =>
- new CommandGroup(
- new Command ("Set as root", ()=> {CrowEditBase.App.CurrentDir = info.FullName;})
- );
- public TreeNode [] GetFileSystemTreeNodeOrdered ()
- => info.GetFileSystemInfos ().OrderBy (f => f.Attributes).ThenBy (f => f.Name)
- .Select (d=> d is DirectoryInfo dinfo ? (TreeNode)new DirectoryNode(dinfo, this) : (TreeNode)new FileNode (d as FileInfo, this)).ToArray ();
-
- }
-
-
-}
+++ /dev/null
-// Copyright (c) 2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.IO;
-using Crow;
-
-namespace CrowEditBase
-{
- public class FileNode : TreeNode
- {
- FileInfo info;
- Document doc;
- public override CommandGroup Commands =>
- new CommandGroup(
- new Command ("Open", Open),
- new Command ("Delete", (sender0) => {
- MessageBox.ShowModal (CrowEditBase.App, MessageBox.Type.YesNo, $"Delete {info.Name}?").Yes += (sender, e) => {
- System.IO.File.Delete(info.FullName);
- Widget listContainer = ((sender0 as Widget).LogicalParent as Widget).DataSource as Widget;
- (listContainer.Parent as Group).RemoveChild(listContainer);
- };
- })
- );
- public FileNode (FileInfo info, TreeNode parent = null) : base (parent) {
- this.info = info;
- }
- public override string Name => info.Name;
-
- public bool IsOpen => doc != null;
-
- public void Open () {
- doc = CrowEditBase.App.OpenFile (info.FullName);
- if (doc is TextDocument td)
- CrowEditBase.App.CurrentDocument = td;
- NotifyValueChanged ("IsOpen", IsOpen);
- }
- public void OnOpenClick (object sender, EventArgs e) => Open();
- }
-
-
-}
+++ /dev/null
-// Copyright (c) 2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Linq;
-using Crow;
-
-namespace CrowEditBase
-{
- public abstract class TreeNode : IValueChange, ISelectable
- {
- #region IValueChange implementation
- public event EventHandler<ValueChangeEventArgs> ValueChanged;
-
- public virtual void NotifyValueChanged (string MemberName, object _value)
- {
- ValueChanged.Raise (this, new ValueChangeEventArgs (MemberName, _value));
- }
- #endregion
-
- #region ISelectable implementation
- public event EventHandler Selected;
- public event EventHandler Unselected;
- public virtual bool IsSelected {
- get { return isSelected; }
- set {
- if (value == isSelected)
- return;
-
- Console.WriteLine ($"TreeNode({this}).IsSelected: {isSelected} -> {value}");
-
- isSelected = value;
-
- NotifyValueChanged ("IsSelected", isSelected);
- }
- }
- #endregion
-
- ObservableList<TreeNode> childs = new ObservableList<TreeNode> ();
-
- public TreeNode () {}
- public TreeNode (TreeNode parent) {
- Parent?.AddChild (this);
- }
-
- protected bool isSelected, isExpanded;
-
- public TreeNode Parent { get; protected set; }
-
- public abstract string Name { get; }
- public ObservableList<TreeNode> Childs {
- get => childs;
- set {
- if (childs == value)
- return;
- childs = value;
- NotifyValueChanged ("Childs", childs);
- }
- }
- public abstract CommandGroup Commands { get; }
-
- public void AddChild (TreeNode pn)
- {
- childs.Add (pn);
- pn.Parent = this;
- }
- public void RemoveChild (TreeNode pn)
- {
- pn.Parent = null;
- childs.Remove (pn);
- }
-
- public virtual bool IsExpanded {
- get { return isExpanded; }
- set {
- if (value == isExpanded)
- return;
- isExpanded = value;
- NotifyValueChanged ("IsExpanded", isExpanded);
- NotifyValueChanged ("IconSub", IconSub);
- }
- }
- public virtual Picture Icon => new SvgPicture ("#Icons.Question.svg");
- public virtual string IconSub => null;
-
- public override string ToString () => Name;
-
- public IEnumerable<TreeNode> Flatten {
- get {
- yield return this;
- foreach (var node in childs.SelectMany (child => child.Flatten))
- yield return node;
- }
- }
-
- public virtual void SortChilds ()
- {
- foreach (TreeNode pn in Childs)
- pn.SortChilds ();
- Childs = new ObservableList<TreeNode> (Childs.OrderBy (c => c, new NodeComparer()));
- }
-
- public class NodeComparer : IComparer<TreeNode>
- {
- public int Compare (TreeNode x, TreeNode y)
- {
- return string.Compare (x.Name, y.Name);
- }
- }
- }
-
-
-}
<?xml version="1.0"?>
<ItemTemplate DataType="Crow.Command" Path="#CrowEdit.ui.MenuButton.template"/>
<ItemTemplate DataType="Crow.CommandGroup" Data="Commands">
- <Popper PopDirection="Right" Caption="{Caption}" IsEnabled="{CanExecute}" Width="Stretched"
+ <Popper PopDirection="Right" Caption="{Caption}" Width="Stretched"
MouseEnter="{Background=${ControlHighlight}}"
MouseLeave="{Background=Transparent}">
<Template>
<ListItem IsVisible="{IsSelected}" IsSelected="{²IsSelected}" Selected="{/tb.HasFocus='true'}">
<VerticalStack Spacing="0">
<HorizontalStack Spacing="0">
- <Editor Name="tb" Multiline="true" Font="consolas, 12" Focusable="true" Height="Stretched" Width="Stretched"
+ <Editor Name="tb" Font="consolas, 12" Focusable="true" Height="Stretched" Width="Stretched"
Document="{}" TextChanged="onTextChanged"/>
<ScrollBar Value="{²../tb.ScrollY}"
LargeIncrement="{../tb.PageHeight}" SmallIncrement="1"
--- /dev/null
+<?xml version="1.0"?>
+<DockWindow Caption="Logs" Width="80%" >
+ <VerticalStack RootDataLevel="true" >
+ <HorizontalStack Spacing="0">
+ <LogViewerWidget Name="log" Lines="{MainLog}" Font="${SmallFont}" MaxScrollX="1000"/>
+ <ScrollBar Name="scrollbarY" Value="{²../log.ScrollY}" CursorRatio="{../log.ChildHeightRatio}" Maximum="{../log.MaxScrollY}" />
+ </HorizontalStack>
+ <ScrollBar Style="HScrollBar" Name="scrollbarX" Value="{²../log.ScrollX}" Maximum="{../log.MaxScrollX}" SmallIncrement="30"/>
+ </VerticalStack>
+</DockWindow>
+
+
<?xml version="1.0"?>
-<DockWindow Caption="Services" Width="60%" >
- <VerticalStack>
- <Label Fit="true" Text="Plugins directory path:"/>
+<DockWindow Caption="Available Plugins" Width="200" >
+ <VerticalStack Margin="5">
+ <Label Fit="true" Text="Plugins directory path:" HorizontalAlignment="Left"/>
<TextBox Height="Fit" Text="{²PluginsDirecory}" Margin="2"/>
+ <ListBox Data="{Plugins}" >
+ <ItemTemplate>
+ <HorizontalStack Height="Fit">
+ <Label Text="{Name}" Width="Stretched"/>
+ <ListBox Data="{Commands}" Fit="true">
+ <Template>
+ <HorizontalStack Name="ItemsContainer" />
+ </Template>
+ <ItemTemplate Path="#CrowEditBase.ui.IconCommand.itmp"/>
+ </ListBox>
+ </HorizontalStack>
+ </ItemTemplate>
+ </ListBox>
</VerticalStack>
</DockWindow>
</ItemTemplate>
</ListBox>-->
- <TreeView Name="treeview" IsRoot="true" RootDataLevel="true" Data="{Projects}" Background="Onyx" >
+ <TreeView Name="treeview" IsRoot="true" RootDataLevel="true" Data="{Projects}" Background="Onyx"
+ SelectedItemChanged="tv_projects_SelectedItemChanged" >
+
<ItemTemplate DataType="default" Data="Childs" Path="#CrowEditBase.ui.TreeExpandable.template" />
<ItemTemplate DataType="CERoslynPlugin.MSBuildProject" Data="Children" Path="#CrowEditBase.ui.TreeExpandable.template" />
<ItemTemplate DataType="CrowEditBase.Project" Data="SubProjects" Path="#CrowEditBase.ui.TreeExpandable.template" />
+
</TreeView>
</DockWindow>