diff --git a/README.md b/README.md index 3fb4e4b..8de7a00 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,5 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/buggins/dlangide?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/buggins/dlangide.svg?branch=master)](https://travis-ci.org/buggins/dlangide) [![PayPayl donate button](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H2ADZV8S6TDHQ "Donate once-off to this project using Paypal") -WTF?! (What The Fork) -====================== -Now you can set dmd includes paths (for correct work with DCD) in workspace settings file (newworkspace.dlangidews) -
-example:
-{
-        "name" : "newworkspace",
-        "description" : null,
-        "projects" : {
-                "newproject" : "newproject/dub.json"
- }, - "includePath" : [ - "/usr/include/dlang/dmd/" - ] -} -
-PS. - Sorry about bad code. Please correct me. - Dlang IDE ========= @@ -82,6 +63,14 @@ Keywan Ghadami improved it to use DCD as a library. Now DCD is embedded into DlangIDE, and no separate executables are needed. +Importing of existing project +============================= + +DlangIDE supports only DUB project format. + +To import existing DUB project, use menu item "File" / "Open project or workspace" and select dub.json or dub.sdl of your project to import. + + Debugger support ================ @@ -193,3 +182,22 @@ OSX: open solution file with Mono-D Choose dlangide as startup project. Coding style: [https://github.com/buggins/dlangui/blob/master/CODING_STYLE.md](https://github.com/buggins/dlangui/blob/master/CODING_STYLE.md) + + +Workspace include path setting +============================== + +Now you can set dmd includes paths (for correct work with DCD) in workspace settings file (newworkspace.dlangidews) +
+example:
+{
+        "name" : "newworkspace",
+        "description" : null,
+        "projects" : {
+                "newproject" : "newproject/dub.json"
+ }, + "includePath" : [ + "/usr/include/dlang/dmd/" + ] +} +
diff --git a/dub.json b/dub.json index 026d787..220ab31 100644 --- a/dub.json +++ b/dub.json @@ -12,7 +12,7 @@ "stringImportPaths": ["views", "views/res", "views/res/i18n", "views/res/mdpi", "views/res/hdpi"], "dependencies": { - "dlangui": "==0.9.99", + "dlangui": "==0.9.120", "dcd": "~>0.9.1" }, diff --git a/src/dlangide/tools/d/dcdinterface.d b/src/dlangide/tools/d/dcdinterface.d index c465825..a8b1a21 100644 --- a/src/dlangide/tools/d/dcdinterface.d +++ b/src/dlangide/tools/d/dcdinterface.d @@ -30,9 +30,14 @@ enum DCDResult : int { FAIL, } +struct CompletionSymbol { + dstring name; + char kind; +} + alias DocCommentsResultSet = Tuple!(DCDResult, "result", string[], "docComments"); alias FindDeclarationResultSet = Tuple!(DCDResult, "result", string, "fileName", ulong, "offset"); -alias CompletionResultSet = Tuple!(DCDResult, "result", dstring[], "output", char[], "completionKinds"); +alias CompletionResultSet = Tuple!(DCDResult, "result", CompletionSymbol[], "output"); class DCDTask { @@ -72,11 +77,14 @@ class DCDTask { if (_cancelled) return; createRequest(); + if (_cancelled) + return; performRequest(); synchronized(this) { if (_cancelled) return; - _guiExecutor.executeInUiThread(&postResults); + if (_guiExecutor) + _guiExecutor.executeInUiThread(&postResults); } } } @@ -183,6 +191,29 @@ class DCDInterface : Thread { return ""; } + /// DCD doc comments task + class ModuleCacheWarmupTask : DCDTask { + + this(CustomEventTarget guiExecutor, string[] importPaths) { + super(guiExecutor, importPaths, null, null, 0); + } + + override void performRequest() { + debug(DCD) Log.d("DCD - warm up module cache with import paths ", _importPaths); + getModuleCache(_importPaths); + debug(DCD) Log.d("DCD - module cache warm up finished"); + } + override void postResults() { + } + } + + DCDTask warmUp(string[] importPaths) { + debug(DCD) Log.d("DCD warmUp: ", importPaths); + ModuleCacheWarmupTask task = new ModuleCacheWarmupTask(null, importPaths); + _queue.put(task); + return task; + } + /// DCD doc comments task class DocCommentsTask : DCDTask { @@ -275,16 +306,17 @@ class DCDInterface : Thread { result.result = DCDResult.SUCCESS; result.output.length = response.completions.length; - result.completionKinds.length = response.completions.length; int i=0; foreach(s;response.completions) { char type = 0; if (i < response.completionKinds.length) type = response.completionKinds[i]; - result.completionKinds[i] = type; - result.output[i++] = to!dstring(s); + result.output[i].kind = type; + result.output[i].name = to!dstring(s); + i++; } - debug(DCD) Log.d("DCD output:\n", response.completions); + postProcessCompletions(result.output); + debug(DCD) Log.d("DCD response:\n", response, "\nCompletion result:\n", result.output); } override void postResults() { _callback(result); @@ -301,6 +333,87 @@ class DCDInterface : Thread { } +int completionTypePriority(char t) { + switch(t) { + case 'c': // - class name + return 10; + case 'i': // - interface name + return 10; + case 's': // - struct name + return 10; + case 'u': // - union name + return 10; + case 'v': // - variable name + return 5; + case 'm': // - member variable name + return 3; + case 'k': // - keyword, built-in version, scope statement + return 20; + case 'f': // - function or method + return 2; + case 'g': // - enum name + return 9; + case 'e': // - enum member + return 8; + case 'P': // - package name + return 30; + case 'M': // - module name + return 20; + case 'a': // - array + return 15; + case 'A': // - associative array + return 15; + case 'l': // - alias name + return 15; + case 't': // - template name + return 14; + case 'T': // - mixin template name + return 14; + default: + return 50; + } +} + +int compareCompletionSymbol(ref CompletionSymbol v1, ref CompletionSymbol v2) { + import std.algorithm : cmp; + int p1 = v1.kind.completionTypePriority; + int p2 = v2.kind.completionTypePriority; + if (p1 < p2) + return -1; + if (p1 > p2) + return 1; + return v1.name.cmp(v2.name); +} + +bool lessCompletionSymbol(ref CompletionSymbol v1, ref CompletionSymbol v2) { + return compareCompletionSymbol(v1, v2) < 0; +} + +void postProcessCompletions(ref CompletionSymbol[] completions) { + import std.algorithm.sorting : sort; + completions.sort!(lessCompletionSymbol); + CompletionSymbol[] res; + bool hasKeywords = false; + bool hasNonKeywords = false; + bool[dstring] found; + foreach(s; completions) { + if (s.kind == 'k') + hasKeywords = true; + else + hasNonKeywords = true; + } + // remove duplicates; remove keywords if non-keyword items are found + foreach(s; completions) { + if (!(s.name in found)) { + found[s.name] = true; + if (s.kind != 'k' || !hasNonKeywords) { + res ~= s; + } + } + } + completions = res; +} + /// to test broken DCD after DUB invocation /// run it after DCD ModuleCache is instantiated diff --git a/src/dlangide/tools/d/deditortool.d b/src/dlangide/tools/d/deditortool.d index bde0927..29fe92d 100644 --- a/src/dlangide/tools/d/deditortool.d +++ b/src/dlangide/tools/d/deditortool.d @@ -63,6 +63,7 @@ class DEditorTool : EditorTool override void cancelGetDocComments() { if (_getDocCommentsTask) { + Log.d("Cancelling getDocComments()"); _getDocCommentsTask.cancel(); _getDocCommentsTask = null; } @@ -70,6 +71,7 @@ class DEditorTool : EditorTool override void cancelGoToDefinition() { if (_goToDefinitionTask) { + Log.d("Cancelling goToDefinition()"); _goToDefinitionTask.cancel(); _goToDefinitionTask = null; } @@ -77,6 +79,7 @@ class DEditorTool : EditorTool override void cancelGetCompletions() { if (_getCompletionsTask) { + Log.d("Cancelling getCompletions()"); _getCompletionsTask.cancel(); _getCompletionsTask = null; } @@ -125,6 +128,7 @@ class DEditorTool : EditorTool DCDTask _getCompletionsTask; override void getCompletions(DSourceEdit editor, TextPosition caretPosition, void delegate(dstring[] completions, string[] icons) callback) { + cancelGetCompletions(); string[] importPaths = editor.importPaths(); string content = toUTF8(editor.text); @@ -134,7 +138,7 @@ class DEditorTool : EditorTool dstring[] labels; foreach(index, label; output.output) { string iconId; - char ch = index < output.completionKinds.length ? output.completionKinds[index] : 0; + char ch = label.kind; switch(ch) { case 'c': // - class name iconId = "symbol-class"; @@ -188,12 +192,11 @@ class DEditorTool : EditorTool iconId = "symbol-mixintemplate"; break; default: + iconId = "symbol-other"; break; } - if (!iconId) - iconId = "symbol-other"; icons ~= iconId; - labels ~= label; + labels ~= label.name; } callback(labels, icons); _getCompletionsTask = null; diff --git a/src/dlangide/tools/d/dsyntax.d b/src/dlangide/tools/d/dsyntax.d index bdcad07..b2d91ed 100644 --- a/src/dlangide/tools/d/dsyntax.d +++ b/src/dlangide/tools/d/dsyntax.d @@ -653,11 +653,15 @@ class SimpleDSyntaxSupport : SyntaxSupport { dstring lineText = _content.line(line); TextLineMeasure lineMeasurement = _content.measureLine(line); TextLineMeasure prevLineMeasurement = _content.measureLine(prevLine); + bool prevLineSpaceOnly = false; + if (prevLineMeasurement.empty && prevLineMeasurement.len) { + prevLineSpaceOnly = true; + } while (prevLineMeasurement.empty && prevLine > 0) { prevLine--; prevLineMeasurement = _content.measureLine(prevLine); } - if (lineMeasurement.firstNonSpaceX >= 0 && lineMeasurement.firstNonSpaceX < prevLineMeasurement.firstNonSpaceX) { + if (lineMeasurement.firstNonSpaceX >= 0 && lineMeasurement.firstNonSpaceX <= prevLineMeasurement.firstNonSpaceX) { dstring prevLineText = _content.line(prevLine); TokenPropString prevLineTokenProps = _content.lineTokenProps(prevLine); dchar lastOpChar = 0; @@ -674,7 +678,16 @@ class SimpleDSyntaxSupport : SyntaxSupport { if (lastOpChar == '{') spacex = _content.nextTab(spacex); dstring txt = _content.fillSpace(spacex); - EditOperation op2 = new EditOperation(EditAction.Replace, TextRange(TextPosition(line, 0), TextPosition(line, lineMeasurement.firstNonSpace >= 0 ? lineMeasurement.firstNonSpace : 0)), [txt]); + dstring[] newContent; + auto startPos = TextPosition(line, 0); + auto endPos = TextPosition(line, lineMeasurement.firstNonSpace >= 0 ? lineMeasurement.firstNonSpace : 0); + if (prevLineSpaceOnly) { + // clear spaces from previous line + startPos.line--; + newContent ~= ""d; + } + newContent ~= txt; + EditOperation op2 = new EditOperation(EditAction.Replace, TextRange(startPos, endPos), newContent); _opInProgress = true; _content.performOperation(op2, source); _opInProgress = false; diff --git a/src/dlangide/ui/commands.d b/src/dlangide/ui/commands.d index 0d438d6..dd11b17 100644 --- a/src/dlangide/ui/commands.d +++ b/src/dlangide/ui/commands.d @@ -46,8 +46,10 @@ enum IDEActions : int { HelpAbout, HelpViewHelp, + HelpDonate, WindowCloseDocument, WindowCloseAllDocuments, + WindowShowHomeScreen, CreateNewWorkspace, AddToCurrentWorkspace, //ProjectFolderAddItem, @@ -129,8 +131,10 @@ const Action ACTION_EDIT_TOGGLE_BLOCK_COMMENT = (new Action(EditorActions.Toggle const Action ACTION_EDIT_PREFERENCES = (new Action(IDEActions.EditPreferences, "MENU_EDIT_PREFERENCES"c, null)).disableByDefault(); const Action ACTION_HELP_ABOUT = new Action(IDEActions.HelpAbout, "MENU_HELP_ABOUT"c); const Action ACTION_HELP_VIEW_HELP = new Action(IDEActions.HelpViewHelp, "MENU_HELP_VIEW_HELP"c); +const Action ACTION_HELP_DONATE = new Action(IDEActions.HelpDonate, "MENU_HELP_DONATE"c); const Action ACTION_WINDOW_CLOSE_DOCUMENT = new Action(IDEActions.WindowCloseDocument, "MENU_WINDOW_CLOSE_DOCUMENT"c, null, KeyCode.KEY_W, KeyFlag.Control); const Action ACTION_WINDOW_CLOSE_ALL_DOCUMENTS = new Action(IDEActions.WindowCloseAllDocuments, "MENU_WINDOW_CLOSE_ALL_DOCUMENTS"c); +const Action ACTION_WINDOW_SHOW_HOME_SCREEN = new Action(IDEActions.WindowShowHomeScreen, "MENU_WINDOW_SHOW_HOME_SCREEN"c); const Action ACTION_CREATE_NEW_WORKSPACE = new Action(IDEActions.CreateNewWorkspace, "OPTION_CREATE_NEW_WORKSPACE"c); const Action ACTION_ADD_TO_CURRENT_WORKSPACE = new Action(IDEActions.AddToCurrentWorkspace, "OPTION_ADD_TO_CURRENT_WORKSPACE"c); diff --git a/src/dlangide/ui/dsourceedit.d b/src/dlangide/ui/dsourceedit.d index fbdeb66..046dd16 100644 --- a/src/dlangide/ui/dsourceedit.d +++ b/src/dlangide/ui/dsourceedit.d @@ -95,6 +95,7 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { @property IDESettings settings() { return _settings; } + protected int _previousFontSizeSetting; void applySettings() { if (!_settings) return; @@ -111,6 +112,12 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { face ~= ","; face ~= DEFAULT_SOURCE_EDIT_FONT_FACES; fontFace = face; + int newFontSizeSetting = _settings.editorFontSize; + bool needChangeFontSize = _previousFontSizeSetting == 0 || (_previousFontSizeSetting != newFontSizeSetting && _previousFontSizeSetting.pointsToPixels == fontSize); + if (needChangeFontSize) { + fontSize = newFontSizeSetting.pointsToPixels; + _previousFontSizeSetting = newFontSizeSetting; + } } protected EditorTool _editorTool; @@ -504,17 +511,25 @@ class DSourceEdit : SourceEdit, EditableContentMarksChangeListener { lineCount = 1; // TODO EditBox widget = new EditBox("docComments"); + widget.styleId = "POPUP_MENU"; widget.readOnly = true; //TextWidget widget = new TextWidget("docComments"); //widget.maxLines = lineCount * 2; //widget.text = "Test popup"d; //text.dup; widget.text = text.dup; + + Point bestSize = widget.fullContentSizeWithBorders(); //widget.layoutHeight = lineCount * widget.fontSize; - widget.minHeight = (lineCount + 1) * widget.fontSize; - widget.maxWidth = width * 3 / 4; - widget.minWidth = width / 8; + if (bestSize.y > height / 3) + bestSize.y = height / 3; + if (bestSize.x > width * 3 / 4) + bestSize.x = width * 3 / 4; + widget.minHeight = bestSize.y; //max((lineCount + 1) * widget.fontSize, bestSize.y); + widget.maxHeight = bestSize.y; + + widget.maxWidth = bestSize.x; //width * 3 / 4; + widget.minWidth = bestSize.x; //width / 8; // widget.layoutWidth = width / 3; - widget.styleId = "POPUP_MENU"; widget.hscrollbarMode = ScrollBarMode.Auto; widget.vscrollbarMode = ScrollBarMode.Auto; uint pos = PopupAlign.Above; diff --git a/src/dlangide/ui/frame.d b/src/dlangide/ui/frame.d index c741a11..d79de53 100644 --- a/src/dlangide/ui/frame.d +++ b/src/dlangide/ui/frame.d @@ -44,7 +44,6 @@ import std.array : empty; import std.string : split; import std.path; -immutable string HELP_PAGE_URL = "https://github.com/buggins/dlangide/wiki"; // TODO: get version from GIT commit //version is now stored in file views/VERSION immutable dstring DLANGIDE_VERSION = toUTF32(import("VERSION")); @@ -667,6 +666,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL _wsPanel.workspaceActionListener = &handleAction; _wsPanel.dockAlignment = DockAlignment.Left; _dockHost.addDockedWindow(_wsPanel); + _wsPanel.visibility = Visibility.Gone; _logPanel = new OutputPanel("output"); _logPanel.compilerLogIssueClickHandler = &onCompilerLogIssueClick; @@ -731,11 +731,9 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL MenuItem windowItem = new MenuItem(new Action(3, "MENU_WINDOW"c)); //windowItem.add(new Action(30, "MENU_WINDOW_PREFERENCES")); - windowItem.add(ACTION_WINDOW_CLOSE_DOCUMENT); - windowItem.add(ACTION_WINDOW_CLOSE_ALL_DOCUMENTS); + windowItem.add(ACTION_WINDOW_CLOSE_DOCUMENT, ACTION_WINDOW_CLOSE_ALL_DOCUMENTS, ACTION_WINDOW_SHOW_HOME_SCREEN); MenuItem helpItem = new MenuItem(new Action(4, "MENU_HELP"c)); - helpItem.add(ACTION_HELP_VIEW_HELP); - helpItem.add(ACTION_HELP_ABOUT); + helpItem.add(ACTION_HELP_VIEW_HELP, ACTION_HELP_ABOUT, ACTION_HELP_DONATE); mainMenuItems.add(fileItem); mainMenuItems.add(editItem); mainMenuItems.add(projectItem); @@ -831,6 +829,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL case IDEActions.FileOpen: case IDEActions.WindowCloseDocument: case IDEActions.WindowCloseAllDocuments: + case IDEActions.WindowShowHomeScreen: case IDEActions.FileOpenWorkspace: // disable when background operation in progress if (!_currentBackgroundOperation) @@ -914,6 +913,9 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL case IDEActions.HelpViewHelp: Platform.instance.openURL(HELP_PAGE_URL); return true; + case IDEActions.HelpDonate: + Platform.instance.openURL(HELP_DONATION_URL); + return true; case IDEActions.HelpAbout: //debug { // testDCDFailAfterThreadCreation(); @@ -1033,6 +1035,9 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL closeAllDocuments(); }); return true; + case IDEActions.WindowShowHomeScreen: + showHomeScreen(); + return true; case IDEActions.FileOpenWorkspace: // Already specified workspace if (!a.stringParam.empty) { @@ -1103,8 +1108,11 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL _logPanel.getTabs.selectTab("search"); if(searchPanel !is null) { searchPanel.focus(); - dstring selectedText = currentEditor.getSelectedText(); + dstring selectedText; + if (currentEditor) + selectedText = currentEditor.getSelectedText(); searchPanel.setSearchText(selectedText); + searchPanel.checkSearchMode(); } return true; case IDEActions.FileNewWorkspace: @@ -1114,7 +1122,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL createNewProject(false); return true; case IDEActions.FileNew: - addProjectItem(a.objectParam); + addProjectItem(cast(Object)a.objectParam); return true; case IDEActions.ProjectFolderRemoveItem: removeProjectItem(a.objectParam); @@ -1228,9 +1236,15 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL } - void addProjectItem(const Object obj) { + /// add new file to project + void addProjectItem(Object obj) { if (currentWorkspace is null) return; + if (obj is null && _wsPanel !is null && !currentEditorSourceFile) { + obj = _wsPanel.selectedProjectItem; + if (!obj) + obj = currentWorkspace.startupProject; + } Project project; ProjectFolder folder; if (cast(Project)obj) { @@ -1320,6 +1334,7 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL if (!project) return; currentWorkspace.startupProject = project; + warmUpImportPaths(project); if (_wsPanel) _wsPanel.updateDefault(); } @@ -1370,8 +1385,10 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL currentTheme.fontSize = settings.uiFontSize; needUpdateTheme = true; } - if (needUpdateTheme) + if (needUpdateTheme) { + Log.d("updating theme after UI font change"); Platform.instance.onThemeChanged(); + } requestLayout(); } @@ -1387,15 +1404,21 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL } const auto msg = UIString.fromId("MSG_OPENED_PROJECT"c); _logPanel.logLine(toUTF32("Project file " ~ project.filename ~ " is opened ok")); + + warmUpImportPaths(project); return true; } + public void warmUpImportPaths(Project project) { + dcdInterface.warmUp(project.importPaths); + } + void openFileOrWorkspace(string filename) { // Open DlangIDE workspace file if (filename.isWorkspaceFile) { Workspace ws = new Workspace(this); if (ws.load(filename)) { - askForUnsavedEdits(delegate() { + askForUnsavedEdits(delegate() { setWorkspace(ws); hideHomeScreen(); // Write workspace to recent workspaces list @@ -1425,6 +1448,11 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL const auto msg = UIString.fromId("MSG_TRY_OPEN_PROJECT"c).value; _logPanel.logLine(msg ~ toUTF32(" " ~ filename)); Project project = new Project(currentWorkspace, filename); + if (!loadProject(project)) { + //window.showMessageBox(UIString.fromId("MSG_OPEN_PROJECT"c), UIString.fromId("ERROR_INVALID_WS_OR_PROJECT_FILE"c)); + //_logPanel.logLine("File is not recognized as DlangIDE project or workspace file"); + return; + } string defWsFile = project.defWorkspaceFile; if (currentWorkspace) { Project existing = currentWorkspace.findProject(project.filename); @@ -1504,8 +1532,14 @@ class IDEFrame : AppFrame, ProgramExecutionStatusListener, BreakpointListChangeL _tabs.setFocus(); } if (ws) { + _wsPanel.visibility = Visibility.Visible; _settings.updateRecentWorkspace(ws.filename); _settings.setRecentPath(ws.dir, "FILE_OPEN_WORKSPACE_PATH"); + if (ws.startupProject) { + warmUpImportPaths(ws.startupProject); + } + } else { + _wsPanel.visibility = Visibility.Gone; } } diff --git a/src/dlangide/ui/homescreen.d b/src/dlangide/ui/homescreen.d index 6e185aa..7c476fa 100644 --- a/src/dlangide/ui/homescreen.d +++ b/src/dlangide/ui/homescreen.d @@ -11,6 +11,10 @@ import dlangui.core.i18n; import std.path; import std.utf : toUTF32; +immutable string HELP_PAGE_URL = "https://github.com/buggins/dlangide/wiki"; +immutable string HELP_DONATION_URL = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H2ADZV8S6TDHQ"; + + class HomeScreen : ScrollWidget { protected IDEFrame _frame; protected HorizontalLayout _content; @@ -66,6 +70,14 @@ class HomeScreen : ScrollWidget { _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DUB_REP"c).value, "http://code.dlang.org/")); _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_UI"c).value, "https://github.com/buggins/dlangui")); _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_IDE"c).value, "https://github.com/buggins/dlangide")); + _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_IDE_HELP"c).value, HELP_PAGE_URL)); + _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_TOUR"c).value, "https://tour.dlang.org/")); + _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_VIBED"c).value, "http://vibed.org/")); + _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_FORUM"c).value, "http://forum.dlang.org/")); + _column1.addChild(new VSpacer()); + _column2.addChild((new TextWidget(null, UIString.fromId("DLANG_IDE_DONATE"c))).fontSize(20).textColor(linkColor)); + _column2.addChild(new UrlImageTextButton(null, UIString.fromId("DLANG_IDE_DONATE_PAYPAL"c).value, HELP_DONATION_URL)); + _column2.addChild(new VSpacer()); contentWidget = _content; } diff --git a/src/dlangide/ui/newfile.d b/src/dlangide/ui/newfile.d index 047297d..ab47509 100644 --- a/src/dlangide/ui/newfile.d +++ b/src/dlangide/ui/newfile.d @@ -122,6 +122,11 @@ class NewFileDlg : Dialog { _edLocation.addFilter(FileFilterEntry(UIString.fromRaw("DlangIDE files"d), "*.dlangidews;*.d;*.dd;*.di;*.ddoc;*.dh;*.json;*.xml;*.ini;*.dt")); _edLocation.caption = "Select directory"d; + _edFileName.editorAction.connect(&onEditorAction); + _edFilePath.editorAction.connect(&onEditorAction); + _edModuleName.editorAction.connect(&onEditorAction); + _edLocation.editorAction.connect(&onEditorAction); + // fill templates dstring[] names; foreach(t; _templates) @@ -156,6 +161,23 @@ class NewFileDlg : Dialog { } + /// called after window with dialog is shown + override void onShow() { + super.onShow(); + _edFileName.selectAll(); + _edFileName.setFocus(); + } + + protected bool onEditorAction(const Action action) { + if (action.id == EditorActions.InsertNewLine) { + if (!validate()) + return false; + close(_buttonActions[0]); + return true; + } + return false; + } + StringListWidget _projectTemplateList; EditBox _templateDescription; DirEditLine _edLocation; @@ -216,7 +238,7 @@ class NewFileDlg : Dialog { if (!exists(_location) || !isDir(_location)) return setError("Location directory does not exist"); - if (_currentTemplate.isModule) { + if (_currentTemplate.kind == FileKind.MODULE || _currentTemplate.kind == FileKind.PACKAGE) { string sourcePath, relativePath; if (!findSource(_location, sourcePath, relativePath)) return setError("Location is outside of source path"); @@ -231,9 +253,16 @@ class NewFileDlg : Dialog { buf ~= ch; } _packageName = buf.dup; - string m = !_packageName.empty ? _packageName ~ '.' ~ _moduleName : _moduleName; + string m; + if (_currentTemplate.kind == FileKind.MODULE) { + m = !_packageName.empty ? _packageName ~ '.' ~ _moduleName : _moduleName; + } else { + m = _packageName; + } _edModuleName.text = toUTF32(m); _packageName = m; + if (_currentTemplate.kind == FileKind.PACKAGE && _packageName.length == 0) + return setError("Package should be located in subdirectory"); } else { string projectPath = _project.dir; if (!isSubdirOf(_location, projectPath)) @@ -248,9 +277,12 @@ class NewFileDlg : Dialog { private FileCreationResult _result; bool createItem() { try { - if (_currentTemplate.isModule) { + if (_currentTemplate.kind == FileKind.MODULE) { string txt = "module " ~ _packageName ~ ";\n\n" ~ _currentTemplate.srccode; write(_fullPathName, txt); + } else if (_currentTemplate.kind == FileKind.PACKAGE) { + string txt = "package " ~ _packageName ~ ";\n\n" ~ _currentTemplate.srccode; + write(_fullPathName, txt); } else { write(_fullPathName, _currentTemplate.srccode); } @@ -284,17 +316,27 @@ class NewFileDlg : Dialog { _currentTemplateIndex = index; _currentTemplate = _templates[index]; _templateDescription.text = _currentTemplate.description; + if (_currentTemplate.kind == FileKind.PACKAGE) { + _edFileName.enabled = false; + _edFileName.text = "package"d; + } else { + if (_edFileName.text == "package") + _edFileName.text = "newfile"; + _edFileName.enabled = true; + } //updateDirLayout(); validate(); } void initTemplates() { _templates ~= new ProjectTemplate("Empty module"d, "Empty D module file."d, ".d", - "\n", true); + "\n", FileKind.MODULE); + _templates ~= new ProjectTemplate("Package"d, "D package."d, ".d", + "\n", FileKind.PACKAGE); _templates ~= new ProjectTemplate("Text file"d, "Empty text file."d, ".txt", - "\n", true); + "\n", FileKind.TEXT); _templates ~= new ProjectTemplate("JSON file"d, "Empty json file."d, ".json", - "{\n}\n", true); + "{\n}\n", FileKind.TEXT); _templates ~= new ProjectTemplate("Vibe-D Diet Template file"d, "Empty Vibe-D Diet Template."d, ".dt", q{ doctype html @@ -303,22 +345,27 @@ html title Hello, World body h1 Hello World -}, true); +}, FileKind.TEXT); } } +enum FileKind { + MODULE, + PACKAGE, + TEXT, +} + class ProjectTemplate { dstring name; dstring description; string fileExtension; string srccode; - bool isModule; - this(dstring name, dstring description, string fileExtension, string srccode, bool isModule) { + FileKind kind; + this(dstring name, dstring description, string fileExtension, string srccode, FileKind kind) { this.name = name; this.description = description; this.fileExtension = fileExtension; this.srccode = srccode; - this.isModule = isModule; + this.kind = kind; } } - diff --git a/src/dlangide/ui/searchPanel.d b/src/dlangide/ui/searchPanel.d index de53897..2d0d533 100644 --- a/src/dlangide/ui/searchPanel.d +++ b/src/dlangide/ui/searchPanel.d @@ -30,10 +30,6 @@ class SearchLogWidget : LogWidget { onThemeChanged(); } - protected dstring _textToHighlight; - @property dstring textToHighlight() { return _textToHighlight; } - @property void textToHighlight(dstring s) { _textToHighlight = s; } - protected uint _filenameColor = 0x0000C0; protected uint _errorColor = 0xFF0000; protected uint _warningColor = 0x606000; @@ -156,6 +152,8 @@ class SearchWidget : TabWidget { SearchLogWidget _resultLog; int _resultLogMatchIndex; ComboBox _searchScope; + ImageCheckButton _cbCaseSensitive; + ImageCheckButton _cbWholeWords; protected IDEFrame _frame; protected SearchMatchList[] _matchedList; @@ -210,6 +208,18 @@ class SearchWidget : TabWidget { _searchScope = new ComboBox("searchScope", ["File"d, "Project"d, "Dependencies"d, "Everywhere"d]); _searchScope.selectedItemIndex = 0; _layout.addChild(_searchScope); + + _cbCaseSensitive = new ImageCheckButton("cbCaseSensitive", "find_case_sensitive"); + _cbCaseSensitive.tooltipText = "EDIT_FIND_CASE_SENSITIVE"; + _cbCaseSensitive.styleId = "TOOLBAR_BUTTON"; + _cbCaseSensitive.checked = true; + _layout.addChild(_cbCaseSensitive); + + _cbWholeWords = new ImageCheckButton("cbWholeWords", "find_whole_words"); + _cbWholeWords.tooltipText = "EDIT_FIND_WHOLE_WORDS"; + _cbWholeWords.styleId = "TOOLBAR_BUTTON"; + _layout.addChild(_cbWholeWords); + addChild(_layout); _resultLog = new SearchLogWidget("SearchLogWidget"); @@ -245,7 +255,7 @@ class SearchWidget : TabWidget { bool findText(dstring source) { Log.d("Finding " ~ source); - _resultLog.textToHighlight = ""d; + _resultLog.setTextToHighlight(""d, 0); _resultLog.text = ""d; _matchedList = []; _resultLogMatchIndex = 0; @@ -254,9 +264,11 @@ class SearchWidget : TabWidget { switch (_searchScope.text) { case "File": - SearchMatchList match = findMatches(_frame.currentEditor.filename, source); - if(match.matches.length > 0) - _matchedList ~= match; + if (_frame.currentEditor) { + SearchMatchList match = findMatches(_frame.currentEditor.filename, source); + if(match.matches.length > 0) + _matchedList ~= match; + } break; case "Project": foreach(Project project; _frame._wsPanel.workspace.projects) { @@ -289,7 +301,7 @@ class SearchWidget : TabWidget { default: assert(0); } - _resultLog.textToHighlight = source; + _resultLog.setTextToHighlight(source, TextSearchFlag.CaseSensitive); return true; } @@ -306,23 +318,41 @@ class SearchWidget : TabWidget { } super.onDraw(buf); } - + + void checkSearchMode() { + if (!_frame.currentEditor && _searchScope.selectedItemIndex == 0) + _searchScope.selectedItemIndex = 1; + } + + uint makeSearchFlags() { + uint res = 0; + if (_cbCaseSensitive.checked) + res |= TextSearchFlag.CaseSensitive; + if (_cbWholeWords.checked) + res |= TextSearchFlag.WholeWords; + return res; + } + //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(); + if (_frame.openSourceFile(matchList.filename)) { + _frame.currentEditor.setTextToHighlight(_findText.text, makeSearchFlags); + _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(); + if (_frame.openSourceFile(matchList.filename)) { + _frame.currentEditor.setCaretPos(match.line, to!int(match.col)); + _frame.currentEditor.setTextToHighlight(_findText.text, makeSearchFlags); + _frame.currentEditor.setFocus(); + } return true; } } diff --git a/src/dlangide/ui/settings.d b/src/dlangide/ui/settings.d index ee7aa42..384c094 100644 --- a/src/dlangide/ui/settings.d +++ b/src/dlangide/ui/settings.d @@ -65,10 +65,6 @@ SettingsPage createSettingsPages() { // Root page SettingsPage res = new SettingsPage("", UIString.fromRaw(""d)); - // Common page - SettingsPage common = res.addChild("common", UIString.fromId("OPTION_COMMON"c)); - common.addCheckbox("common/autoOpenLastProject", UIString.fromId("OPTION_AUTO_OPEN_LAST_PROJECT"c)); - // UI settings page SettingsPage ui = res.addChild("interface", UIString.fromId("OPTION_INTERFACE"c)); ui.addStringComboBox("interface/theme", UIString.fromId("OPTION_THEME"c), [ @@ -126,6 +122,8 @@ SettingsPage createSettingsPages() { // editor font faces texted.addStringComboBox("editors/textEditor/fontFace", UIString.fromId("OPTION_FONT_FACE"c), createFaceList(true)); + texted.addIntComboBox("editors/textEditor/fontSize", UIString.fromId("OPTION_FONT_SIZE"c), + createIntValueList([6,7,8,9,10,11,12,14,16,18,20,22,24,26,28,30,32])); texted.addNumberEdit("editors/textEditor/tabSize", UIString.fromId("OPTION_TAB"c), 1, 16, 4); texted.addCheckbox("editors/textEditor/useSpacesForTabs", UIString.fromId("OPTION_USE_SPACES"c)); @@ -134,6 +132,10 @@ SettingsPage createSettingsPages() { texted.addCheckbox("editors/textEditor/showWhiteSpaceMarks", UIString.fromId("OPTION_SHOW_SPACES"c)); texted.addCheckbox("editors/textEditor/showTabPositionMarks", UIString.fromId("OPTION_SHOW_TABS"c)); + // Common page + SettingsPage common = res.addChild("common", UIString.fromId("OPTION_COMMON"c)); + common.addCheckbox("common/autoOpenLastProject", UIString.fromId("OPTION_AUTO_OPEN_LAST_PROJECT"c)); + SettingsPage dlang = res.addChild("dlang", UIString.fromRaw("D"d)); SettingsPage dub = dlang.addChild("dlang/dub", UIString.fromRaw("DUB"d)); diff --git a/src/dlangide/ui/wspanel.d b/src/dlangide/ui/wspanel.d index 958daea..5c5e59e 100644 --- a/src/dlangide/ui/wspanel.d +++ b/src/dlangide/ui/wspanel.d @@ -149,6 +149,17 @@ class WorkspacePanel : DockWindow { return _workspace; } + /// returns currently selected project item + @property ProjectItem selectedProjectItem() { + TreeItem ti = _tree.items.selectedItem; + if (!ti) + return null; + Object obj = ti.objectParam; + if (!obj) + return null; + return cast(ProjectItem)obj; + } + ProjectSourceFile findSourceFileItem(string filename, bool fullFileName=true) { if (_workspace) return _workspace.findSourceFileItem(filename, fullFileName); diff --git a/src/dlangide/workspace/idesettings.d b/src/dlangide/workspace/idesettings.d index f70e6a8..2e4f4f1 100644 --- a/src/dlangide/workspace/idesettings.d +++ b/src/dlangide/workspace/idesettings.d @@ -24,6 +24,7 @@ class IDESettings : SettingsFile { ed.setBooleanDef("showWhiteSpaceMarks", true); ed.setBooleanDef("showTabPositionMarks", true); ed.setStringDef("fontFace", "Default"); + ed.setIntegerDef("fontSize", 11); Setting ui = uiSettings(); ui.setStringDef("theme", "ide_theme_default"); ui.setStringDef("language", "en"); @@ -170,10 +171,10 @@ class IDESettings : SettingsFile { @property bool showTabPositionMarks() { return editorSettings.getBoolean("showTabPositionMarks", true); } /// set tab position marks enabled flag @property IDESettings showTabPositionMarks(bool enabled) { editorSettings.setBoolean("showTabPositionMarks", enabled); return this; } - /// string value of font face + /// string value of font face in text editors @property string editorFontFace() { return editorSettings.getString("fontFace", "Default"); } - - + /// int value of font size in text editors + @property int editorFontSize() { return cast(int)editorSettings.getInteger("fontSize", 11); } /// true if smart indents are enabled @property bool smartIndentsAfterPaste() { return editorSettings.getBoolean("smartIndentsAfterPaste", true); } diff --git a/src/dlangide/workspace/project.d b/src/dlangide/workspace/project.d index fc2db3b..9e1f71b 100644 --- a/src/dlangide/workspace/project.d +++ b/src/dlangide/workspace/project.d @@ -202,6 +202,17 @@ class ProjectFolder : ProjectItem { _children.remove(i); } } + sortItems(); + } + + /// predicate for sorting project items + static bool compareProjectItemsLess(ProjectItem item1, ProjectItem item2) { + return ((item1.isFolder && !item2.isFolder) || ((item1.isFolder == item2.isFolder) && (item1.name < item2.name))); + } + + void sortItems() { + import std.algorithm.sorting : sort; + sort!compareProjectItemsLess(_children.asArray); } string relativeToAbsolutePath(string path) { @@ -218,6 +229,7 @@ class ProjectFolder : ProjectItem { } } + /// Project source file class ProjectSourceFile : ProjectItem { this(string filename) { diff --git a/views/VERSION b/views/VERSION index 7114f72..758f9c1 100644 --- a/views/VERSION +++ b/views/VERSION @@ -1 +1 @@ -v0.7.52 \ No newline at end of file +v0.7.71 \ No newline at end of file diff --git a/views/res/i18n/en.ini b/views/res/i18n/en.ini index 1f22da7..278f1ed 100644 --- a/views/res/i18n/en.ini +++ b/views/res/i18n/en.ini @@ -1,7 +1,7 @@ ABOUT=About DlangIDE HOME=DlangIDE Home DESCRIPTION=D language IDE written in D -COPYRIGHT=(c) Vadim Lopatin 2015 +COPYRIGHT=(c) Vadim Lopatin 2017 START_WITH=Start with: RECENT=Recent: NO_RECENT=No recent items @@ -10,6 +10,12 @@ D_LANG=D Programming Language DUB_REP=DUB repository DLANG_UI=DLangUI on GitHub DLANG_IDE=DLangIDE on GitHub +DLANG_IDE_HELP=DLangIDE online documentation +DLANG_TOUR=DLang Tour +DLANG_VIBED=Vibe-D +DLANG_FORUM=Dlang Forum +DLANG_IDE_DONATE=Support DlangIDE +DLANG_IDE_DONATE_PAYPAL=Donate via PayPal EXIT=Exit ALL_FILES=All files @@ -86,10 +92,12 @@ MENU_WINDOW=&Window MENU_WINDOW_PREFERENCES=&Preferences MENU_WINDOW_CLOSE_DOCUMENT=Close document MENU_WINDOW_CLOSE_ALL_DOCUMENTS=Close all documents +MENU_WINDOW_SHOW_HOME_SCREEN=Show home screen MENU_HELP=&Help -MENU_HELP_VIEW_HELP=&View help +MENU_HELP_VIEW_HELP=Online help MENU_HELP_ABOUT=&About +MENU_HELP_DONATE=Donate via PayPal MENU_VIEW=&View MENU_VIEW_LANGUAGE=Interface &Language diff --git a/views/res/i18n/ru.ini b/views/res/i18n/ru.ini index 408d04e..3f32da5 100644 --- a/views/res/i18n/ru.ini +++ b/views/res/i18n/ru.ini @@ -1,7 +1,7 @@ ABOUT=О DlangIDE HOME=Домашняя страница DlangIDE DESCRIPTION=IDE для языка D написанная на D -COPYRIGHT=(c) Вадим Лопатин 2015 +COPYRIGHT=(c) Вадим Лопатин 2017 START_WITH=Начать с: RECENT=Недавнее: NO_RECENT=Нет недавно открытых файлов/проектов @@ -10,6 +10,12 @@ D_LANG=Язык программирования D DUB_REP=Хранилище DUB DLANG_UI=DLangUI на GitHub DLANG_IDE=DLangIDE на GitHub +DLANG_IDE_HELP=DLangIDE документация +DLANG_TOUR=Интерактивный тур D +DLANG_VIBED=Vibe-D +DLANG_FORUM=Форум D +DLANG_IDE_DONATE=Поддержать DlangIDE +DLANG_IDE_DONATE_PAYPAL=PayPal EXIT=Выход ALL_FILES=Все файлы @@ -86,10 +92,12 @@ MENU_WINDOW=&Окно MENU_WINDOW_PREFERENCES=&Настройки MENU_WINDOW_CLOSE_DOCUMENT=Закрыть документ MENU_WINDOW_CLOSE_ALL_DOCUMENTS=Закрыть все документы +MENU_WINDOW_SHOW_HOME_SCREEN=Домашняя страница MENU_HELP=&Справка -MENU_HELP_VIEW_HELP=&Просмотр справки +MENU_HELP_VIEW_HELP=Онлайн справка MENU_HELP_ABOUT=&О программе +MENU_HELP_DONATE=Поддержать проект через PayPal MENU_VIEW=&Вид MENU_VIEW_LANGUAGE=&Язык интерфейса