mirror of https://github.com/buggins/dlangide.git
commit
d9d61c7c46
|
@ -44,6 +44,7 @@ enum IDEActions : int {
|
||||||
GoToDefinition,
|
GoToDefinition,
|
||||||
GetCompletionSuggestions,
|
GetCompletionSuggestions,
|
||||||
InsertCompletion,
|
InsertCompletion,
|
||||||
|
FindText,
|
||||||
}
|
}
|
||||||
|
|
||||||
__gshared static this() {
|
__gshared static this() {
|
||||||
|
@ -101,3 +102,4 @@ const Action ACTION_ADD_TO_CURRENT_WORKSPACE = new Action(IDEActions.AddToCurren
|
||||||
|
|
||||||
const Action ACTION_GO_TO_DEFINITION = (new Action(IDEActions.GoToDefinition, "GO_TO_DEFINITION"c, ""c, KeyCode.KEY_G, KeyFlag.Control)).addAccelerator(KeyCode.F12, 0).disableByDefault();
|
const Action ACTION_GO_TO_DEFINITION = (new Action(IDEActions.GoToDefinition, "GO_TO_DEFINITION"c, ""c, KeyCode.KEY_G, KeyFlag.Control)).addAccelerator(KeyCode.F12, 0).disableByDefault();
|
||||||
const Action ACTION_GET_COMPLETIONS = (new Action(IDEActions.GetCompletionSuggestions, "SHOW_COMPLETIONS"c, ""c, KeyCode.KEY_G, KeyFlag.Control|KeyFlag.Shift)).addAccelerator(KeyCode.SPACE, KeyFlag.Control).disableByDefault();
|
const Action ACTION_GET_COMPLETIONS = (new Action(IDEActions.GetCompletionSuggestions, "SHOW_COMPLETIONS"c, ""c, KeyCode.KEY_G, KeyFlag.Control|KeyFlag.Shift)).addAccelerator(KeyCode.SPACE, KeyFlag.Control).disableByDefault();
|
||||||
|
const Action ACTION_FIND_TEXT = (new Action(IDEActions.FindText, "FIND_TEXT"c, ""c, KeyCode.KEY_F, KeyFlag.Control));
|
||||||
|
|
|
@ -385,7 +385,7 @@ class IDEFrame : AppFrame {
|
||||||
|
|
||||||
MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT"));
|
MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT"));
|
||||||
editItem.add(ACTION_EDIT_COPY, ACTION_EDIT_PASTE,
|
editItem.add(ACTION_EDIT_COPY, ACTION_EDIT_PASTE,
|
||||||
ACTION_EDIT_CUT, ACTION_EDIT_UNDO, ACTION_EDIT_REDO);
|
ACTION_EDIT_CUT, ACTION_EDIT_UNDO, ACTION_EDIT_REDO, ACTION_FIND_TEXT);
|
||||||
MenuItem editItemAdvanced = new MenuItem(new Action(221, "MENU_EDIT_ADVANCED"));
|
MenuItem editItemAdvanced = new MenuItem(new Action(221, "MENU_EDIT_ADVANCED"));
|
||||||
editItemAdvanced.add(ACTION_EDIT_INDENT, ACTION_EDIT_UNINDENT, ACTION_EDIT_TOGGLE_LINE_COMMENT, ACTION_EDIT_TOGGLE_BLOCK_COMMENT, ACTION_GO_TO_DEFINITION, ACTION_GET_COMPLETIONS);
|
editItemAdvanced.add(ACTION_EDIT_INDENT, ACTION_EDIT_UNINDENT, ACTION_EDIT_TOGGLE_LINE_COMMENT, ACTION_EDIT_TOGGLE_BLOCK_COMMENT, ACTION_GO_TO_DEFINITION, ACTION_GET_COMPLETIONS);
|
||||||
editItem.add(editItemAdvanced);
|
editItem.add(editItemAdvanced);
|
||||||
|
@ -626,6 +626,17 @@ class IDEFrame : AppFrame {
|
||||||
case IDEActions.EditPreferences:
|
case IDEActions.EditPreferences:
|
||||||
showPreferences();
|
showPreferences();
|
||||||
return true;
|
return true;
|
||||||
|
case IDEActions.FindText:
|
||||||
|
Log.d("Opening Search Field");
|
||||||
|
import dlangide.ui.searchPanel;
|
||||||
|
int searchPanelIndex = _logPanel.getTabs.tabIndex("search");
|
||||||
|
if(searchPanelIndex == -1) {
|
||||||
|
SearchWidget searchPanel = new SearchWidget("search", this);
|
||||||
|
_logPanel.getTabs.addTab( searchPanel, "Search"d, null, true);
|
||||||
|
}
|
||||||
|
_logPanel.getTabs.selectTab("search");
|
||||||
|
//TODO: Focus search field
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.handleAction(a);
|
return super.handleAction(a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,19 @@ module dlangide.ui.outputpanel;
|
||||||
import dlangui;
|
import dlangui;
|
||||||
import dlangide.workspace.workspace;
|
import dlangide.workspace.workspace;
|
||||||
import dlangide.workspace.project;
|
import dlangide.workspace.project;
|
||||||
|
import dlangide.ui.frame;
|
||||||
|
|
||||||
import std.utf;
|
import std.utf;
|
||||||
import std.regex;
|
import std.regex;
|
||||||
import std.algorithm : startsWith;
|
import std.algorithm : startsWith;
|
||||||
|
import std.string;
|
||||||
|
|
||||||
/// event listener to navigate by error/warning position
|
/// event listener to navigate by error/warning position
|
||||||
interface CompilerLogIssueClickHandler {
|
interface CompilerLogIssueClickHandler {
|
||||||
bool onCompilerLogIssueClick(dstring filename, int line, int column);
|
bool onCompilerLogIssueClick(dstring filename, int line, int column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Log widget with parsing of compiler output
|
/// Log widget with parsing of compiler output
|
||||||
class CompilerLogWidget : LogWidget {
|
class CompilerLogWidget : LogWidget {
|
||||||
|
|
||||||
|
@ -113,20 +116,42 @@ class OutputPanel : DockWindow {
|
||||||
|
|
||||||
protected CompilerLogWidget _logWidget;
|
protected CompilerLogWidget _logWidget;
|
||||||
|
|
||||||
|
TabWidget _tabs;
|
||||||
|
|
||||||
|
@property TabWidget getTabs() { return _tabs;}
|
||||||
|
|
||||||
this(string id) {
|
this(string id) {
|
||||||
super(id);
|
_showCloseButton = false;
|
||||||
_caption.text = "Output"d;
|
|
||||||
dockAlignment = DockAlignment.Bottom;
|
dockAlignment = DockAlignment.Bottom;
|
||||||
|
super(id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected Widget createBodyWidget() {
|
override protected Widget createBodyWidget() {
|
||||||
|
_tabs = new TabWidget("OutputPanelTabs");
|
||||||
|
_tabs.setStyles(STYLE_DOCK_HOST_BODY, STYLE_TAB_UP_DARK, STYLE_TAB_UP_BUTTON_DARK, STYLE_TAB_UP_BUTTON_DARK_TEXT);
|
||||||
|
|
||||||
_logWidget = new CompilerLogWidget("logwidget");
|
_logWidget = new CompilerLogWidget("logwidget");
|
||||||
_logWidget.readOnly = true;
|
_logWidget.readOnly = true;
|
||||||
_logWidget.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT);
|
_logWidget.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
||||||
_logWidget.compilerLogIssueClickHandler = &onIssueClick;
|
_logWidget.compilerLogIssueClickHandler = &onIssueClick;
|
||||||
return _logWidget;
|
|
||||||
|
_tabs.addTab(_logWidget, "Compiler Log"d);
|
||||||
|
_tabs.selectTab("logwidget");
|
||||||
|
|
||||||
|
return _tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override protected void init() {
|
||||||
|
|
||||||
|
styleId = STYLE_DOCK_WINDOW;
|
||||||
|
_bodyWidget = createBodyWidget();
|
||||||
|
_bodyWidget.styleId = STYLE_DOCK_WINDOW_BODY;
|
||||||
|
addChild(_bodyWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Refactor OutputPanel to expose CompilerLogWidget
|
||||||
|
|
||||||
void appendText(string category, dstring msg) {
|
void appendText(string category, dstring msg) {
|
||||||
_logWidget.appendText(msg);
|
_logWidget.appendText(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
module dlangide.ui.searchPanel;
|
||||||
|
|
||||||
|
|
||||||
|
import dlangui;
|
||||||
|
|
||||||
|
import dlangide.ui.frame;
|
||||||
|
import dlangide.ui.wspanel;
|
||||||
|
import dlangide.workspace.workspace;
|
||||||
|
import dlangide.workspace.project;
|
||||||
|
|
||||||
|
import std.string;
|
||||||
|
import std.conv;
|
||||||
|
|
||||||
|
interface SearchResultClickHandler {
|
||||||
|
bool onSearchResultClick(int line);
|
||||||
|
}
|
||||||
|
|
||||||
|
//LogWidget with highlighting for search results.
|
||||||
|
class SearchLogWidget : LogWidget {
|
||||||
|
|
||||||
|
//Sends which line was clicked.
|
||||||
|
Signal!SearchResultClickHandler searchResultClickHandler;
|
||||||
|
|
||||||
|
this(string ID){
|
||||||
|
super(ID);
|
||||||
|
scrollLock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected CustomCharProps[] handleCustomLineHighlight(int line, dstring txt, ref CustomCharProps[] buf) {
|
||||||
|
uint defColor = textColor;
|
||||||
|
const uint filenameColor = 0x0000C0;
|
||||||
|
const uint errorColor = 0xFF0000;
|
||||||
|
const uint warningColor = 0x606000;
|
||||||
|
const uint deprecationColor = 0x802040;
|
||||||
|
uint flags = 0;
|
||||||
|
if (buf.length < txt.length)
|
||||||
|
buf.length = txt.length;
|
||||||
|
|
||||||
|
//Highlights the filename
|
||||||
|
if(txt.startsWith("Matches in ")) {
|
||||||
|
CustomCharProps[] colors = buf[0..txt.length];
|
||||||
|
uint cl = defColor;
|
||||||
|
flags = 0;
|
||||||
|
for (int i = 0; i < txt.length; i++) {
|
||||||
|
dstring rest = txt[i..$];
|
||||||
|
if(i == 11) {
|
||||||
|
cl = filenameColor;
|
||||||
|
flags = TextFlag.Underline;
|
||||||
|
}
|
||||||
|
colors[i].color = cl;
|
||||||
|
colors[i].textFlags = flags;
|
||||||
|
}
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
//Highlight line and collumn
|
||||||
|
else {
|
||||||
|
CustomCharProps[] colors = buf[0..txt.length];
|
||||||
|
uint cl = filenameColor;
|
||||||
|
flags = TextFlag.Underline;
|
||||||
|
for (int i = 0; i < txt.length; i++) {
|
||||||
|
dstring rest = txt[i..$];
|
||||||
|
if (rest.startsWith(" -->"d)) {
|
||||||
|
cl = warningColor;
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
if(i == 4) {
|
||||||
|
cl = errorColor;
|
||||||
|
flags = TextFlag.Underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
colors[i].color = cl;
|
||||||
|
colors[i].textFlags = flags;
|
||||||
|
|
||||||
|
//Colors to apply in following iterations of the loop.
|
||||||
|
if(rest.startsWith("]")) {
|
||||||
|
cl = defColor;
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override bool onMouseEvent(MouseEvent event) {
|
||||||
|
super.onMouseEvent(event);
|
||||||
|
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
|
||||||
|
int line = _caretPos.line;
|
||||||
|
if (searchResultClickHandler.assigned) {
|
||||||
|
searchResultClickHandler(line);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SearchWidget : TabWidget {
|
||||||
|
HorizontalLayout _layout;
|
||||||
|
EditLine _findText;
|
||||||
|
SearchLogWidget _resultLog;
|
||||||
|
ComboBox _searchScope;
|
||||||
|
|
||||||
|
protected IDEFrame _frame;
|
||||||
|
protected SearchMatchList[] _matchedList;
|
||||||
|
|
||||||
|
struct SearchMatch {
|
||||||
|
int line;
|
||||||
|
long col;
|
||||||
|
dstring lineContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SearchMatchList {
|
||||||
|
string filename;
|
||||||
|
SearchMatch[] matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
this(string ID, IDEFrame frame) {
|
||||||
|
super(ID);
|
||||||
|
_frame = frame;
|
||||||
|
|
||||||
|
layoutHeight(FILL_PARENT);
|
||||||
|
|
||||||
|
//Remove title, more button
|
||||||
|
removeAllChildren();
|
||||||
|
|
||||||
|
_layout = new HorizontalLayout();
|
||||||
|
_layout.addChild(new TextWidget("FindLabel", "Find: "d));
|
||||||
|
|
||||||
|
_findText = new EditLine();
|
||||||
|
_findText.padding(Rect(5,4,50,4));
|
||||||
|
_findText.layoutWidth(400);
|
||||||
|
_layout.addChild(_findText);
|
||||||
|
|
||||||
|
auto goButton = new ImageButton("findTextButton", "edit-redo");
|
||||||
|
goButton.addOnClickListener( delegate(Widget) {
|
||||||
|
findText(_findText.text);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
_layout.addChild(goButton);
|
||||||
|
|
||||||
|
_searchScope = new ComboBox("searchScope", ["File"d, "Project"d, "Dependencies"d, "Everywhere"d]);
|
||||||
|
_searchScope.selectedItemIndex = 0;
|
||||||
|
_layout.addChild(_searchScope);
|
||||||
|
addChild(_layout);
|
||||||
|
|
||||||
|
_resultLog = new SearchLogWidget("SearchLogWidget");
|
||||||
|
_resultLog.searchResultClickHandler = &onMatchClick;
|
||||||
|
_resultLog.layoutHeight(FILL_PARENT);
|
||||||
|
addChild(_resultLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Recursively search for text in projectItem
|
||||||
|
void searchInProject(ProjectItem project, dstring text) {
|
||||||
|
if (project.isFolder == true) {
|
||||||
|
ProjectFolder projFolder = cast(ProjectFolder) project;
|
||||||
|
for (int i = 0; i < projFolder.childCount; i++) {
|
||||||
|
searchInProject(projFolder.child(i), text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.d("Searching in: " ~ project.filename);
|
||||||
|
EditableContent content = new EditableContent(true);
|
||||||
|
content.load(project.filename);
|
||||||
|
SearchMatchList match;
|
||||||
|
match.filename = project.filename;
|
||||||
|
|
||||||
|
foreach(int lineIndex, dstring line; content.lines) {
|
||||||
|
auto colIndex = line.indexOf(text);
|
||||||
|
if (colIndex != -1) {
|
||||||
|
match.matches ~= SearchMatch(lineIndex, colIndex, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match.matches.length > 0) {
|
||||||
|
_matchedList ~= match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findText(dstring source) {
|
||||||
|
Log.d("Finding " ~ source);
|
||||||
|
|
||||||
|
_resultLog.text = ""d;
|
||||||
|
_matchedList = [];
|
||||||
|
|
||||||
|
switch (_searchScope.text) {
|
||||||
|
case "File": //File
|
||||||
|
auto currentFileItem = _frame._wsPanel.workspace.findSourceFileItem(_frame.currentEditor.filename);
|
||||||
|
if(currentFileItem !is null)
|
||||||
|
searchInProject(currentFileItem, source);
|
||||||
|
break;
|
||||||
|
case "Project": //Project
|
||||||
|
foreach(Project project; _frame._wsPanel.workspace.projects) {
|
||||||
|
if(!project.isDependency)
|
||||||
|
searchInProject(project.items, source);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Dependencies": //Dependencies
|
||||||
|
foreach(Project project; _frame._wsPanel.workspace.projects) {
|
||||||
|
if(project.isDependency)
|
||||||
|
searchInProject(project.items, source);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Everywhere": //Everywhere
|
||||||
|
foreach(Project project; _frame._wsPanel.workspace.projects) {
|
||||||
|
searchInProject(project.items, source);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_matchedList.length == 0) {
|
||||||
|
_resultLog.appendText(to!dstring("No matches found.\n"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach(SearchMatchList fileMatchList; _matchedList) {
|
||||||
|
_resultLog.appendText("Matches in "d ~ to!dstring(fileMatchList.filename) ~ '\n');
|
||||||
|
foreach(SearchMatch match; fileMatchList.matches) {
|
||||||
|
_resultLog.appendText(" --> ["d ~ to!dstring(match.line+1) ~ ":"d ~ to!dstring(match.col) ~ "]" ~ match.lineContent ~"\n"d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find the match/matchList that corrosponds to the line in _resultLog
|
||||||
|
bool onMatchClick(int line) {
|
||||||
|
line++;
|
||||||
|
foreach(matchList; _matchedList){
|
||||||
|
line--;
|
||||||
|
if (line == 0) {
|
||||||
|
_frame.openSourceFile(matchList.filename);
|
||||||
|
_frame.currentEditor.setFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
foreach(match; matchList.matches) {
|
||||||
|
line--;
|
||||||
|
if (line == 0) {
|
||||||
|
_frame.openSourceFile(matchList.filename);
|
||||||
|
_frame.currentEditor.setCaretPos(match.line, to!int(match.col));
|
||||||
|
_frame.currentEditor.setFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,7 +63,7 @@ class ProjectItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns true if item is folder
|
/// returns true if item is folder
|
||||||
@property bool isFolder() {
|
@property const bool isFolder() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/// returns child object count
|
/// returns child object count
|
||||||
|
@ -84,7 +84,7 @@ class ProjectFolder : ProjectItem {
|
||||||
super(filename);
|
super(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@property override bool isFolder() {
|
@property override const bool isFolder() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@property override int childCount() {
|
@property override int childCount() {
|
||||||
|
|
|
@ -56,9 +56,11 @@ MENU_WINDOW_CLOSE_ALL_DOCUMENTS=Close All Documents
|
||||||
MENU_HELP=&HELP
|
MENU_HELP=&HELP
|
||||||
MENU_HELP_VIEW_HELP=&View help
|
MENU_HELP_VIEW_HELP=&View help
|
||||||
MENU_HELP_ABOUT=&About
|
MENU_HELP_ABOUT=&About
|
||||||
|
MENU_NAVIGATE=NAVIGATE
|
||||||
|
|
||||||
GO_TO_DEFINITION=Go To Definition
|
GO_TO_DEFINITION=Go To Definition
|
||||||
SHOW_COMPLETIONS=Get Autocompletions
|
SHOW_COMPLETIONS=Get Autocompletions
|
||||||
MENU_NAVIGATE=NAVIGATE
|
FIND_TEXT=Find text
|
||||||
|
|
||||||
|
|
||||||
TAB_LONG_LIST=Long list
|
TAB_LONG_LIST=Long list
|
||||||
|
|
Loading…
Reference in New Issue