Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use of DCD for "Go To Definition" and Code Completion functionality plus other changes. #29

Merged
merged 15 commits into from
Feb 17, 2015
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/dlangide/builders/extprocess.d
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ class ExternalProcess {
params ~= _program;
params ~= _args;
if (!_stderr)
redirect = Redirect.stdout | Redirect.stderrToStdout; //Redirect.stdin |
redirect = Redirect.stdout | Redirect.stderrToStdout | Redirect.stdin;
else
redirect = Redirect.all;
Log.i("Trying to run program ", _program, " with args ", _args);
Expand Down Expand Up @@ -393,4 +393,14 @@ class ExternalProcess {
}
return _state;
}

void write(dstring data) {
if(_state == ExternalProcessState.Error || _state == ExternalProcessState.None || _state == ExternalProcessState.Stopped) {
return;
}
else {
_pipes.stdin.write(data);
_pipes.stdin.close();
}
}
}
23 changes: 23 additions & 0 deletions src/dlangide/tools/EditorTool.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module dlangide.tools.editorTool;



import dlangui.widgets.editors;
import dlangui.core.types;
import dlangide.ui.frame;
import dlangide.ui.dsourceedit;

public import dlangide.tools.d.editorTool;

class EditorTool
{
this(IDEFrame frame) {
_frame = frame;
}
//Since files might be unsaved, we must send all the text content.
abstract bool goToDefinition(DSourceEdit editor, TextPosition caretPosition);
abstract dstring[] getCompletions(DSourceEdit editor, TextPosition caretPosition);

protected IDEFrame _frame;

}
26 changes: 26 additions & 0 deletions src/dlangide/tools/d/DCDInterface.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module dlangide.tools.d.DCDInterface;

import dlangide.builders.extprocess;

//Interface to DCD
//TODO: Check if server is running, start server if needed etc.
class DCDInterface {
ExternalProcess dcdProcess;
this() {
dcdProcess = new ExternalProcess();
}
bool execute(char[][] arguments ,ref dstring output, dstring input) {
ProtectedTextStorage stdoutTarget = new ProtectedTextStorage();
ExternalProcess dcdProcess = new ExternalProcess();
//TODO: Working Directory, where is that?
//TODO: Inform user when dcd-client is not available.
dcdProcess.run("dcd-client".dup, arguments, "/usr/bin".dup, stdoutTarget);
dcdProcess.write(input);

while(dcdProcess.poll() == ExternalProcessState.Running){ }

output = stdoutTarget.readText();
return true;
}

}
146 changes: 146 additions & 0 deletions src/dlangide/tools/d/DEditorTool.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
module dlangide.tools.d.editorTool;

import dlangide.tools.editorTool;
import dlangide.tools.d.DCDInterface;
import dlangide.ui.dsourceedit;
import dlangui.widgets.editors;
import dlangide.ui.frame;
import std.stdio;
import std.string;
import dlangui.core.logger;

import std.conv;

class DEditorTool : EditorTool
{


this(IDEFrame frame) {
_dcd = new DCDInterface();
super(frame);
}

override bool goToDefinition(DSourceEdit editor, TextPosition caretPosition) {


auto content = editor.text();
auto byteOffset = caretPositionToByteOffset(content, caretPosition);

char[][] arguments = ["-l".dup, "-c".dup];
arguments ~= [to!(char[])(byteOffset)];
//arguments ~= [to!(char[])(editor.projectSourceFile.filename())];

dstring output;
_dcd.execute(arguments, output, content);

string[] outputLines = to!string(output).splitLines();
Log.d("DCD:", outputLines);

foreach(string outputLine ; outputLines) {
if(outputLine.indexOf("Not Found".dup) == -1) {
auto split = outputLine.indexOf("\t");
if(split == -1) {
Log.d("DCD output format error.");
break;
}
if(indexOf(outputLine[0 .. split],"stdin".dup) != -1) {
Log.d("Declaration is in current file. Can jump to it.");
auto target = to!int(outputLine[split+1 .. $]);
auto destPos = byteOffsetToCaret(content, target);
editor.setCaretPos(destPos.line,destPos.pos);
}
else {
auto filename = outputLine[0 .. split];
if(_frame !is null) {
_frame.openSourceFile(filename);
auto target = to!int(outputLine[split+1 .. $]);
auto destPos = byteOffsetToCaret(_frame.currentEditor.text(), target);

_frame.currentEditor.setCaretPos(destPos.line,destPos.pos);
}
}
}
}
return true;
}

override dstring[] getCompletions(DSourceEdit editor, TextPosition caretPosition) {
auto content = editor.text();
auto byteOffset = caretPositionToByteOffset(content, caretPosition);

char[][] arguments = ["-c".dup];
arguments ~= [to!(char[])(byteOffset)];
//arguments ~= [to!(char[])(editor.projectSourceFile.filename())];

dstring output;
_dcd.execute(arguments, output, content);

char[] state = "".dup;
dstring[] suggestions;
foreach(dstring outputLine ; output.splitLines()) {
if(outputLine == "identifiers") {
state = "identifiers".dup;
}
else if(outputLine == "calltips") {
state = "calltips".dup;
}
else {
auto split = outputLine.indexOf("\t");
if(split < 0) {
break;
}
if(state == "identifiers") {
suggestions ~= outputLine[0 .. split];
}
}
}
return suggestions;
}


private:
DCDInterface _dcd;

int caretPositionToByteOffset(dstring content, TextPosition caretPosition) {
auto line = 0;
auto pos = 0;
auto bytes = 0;
foreach(c; content) {
bytes++;
if(c == '\n') {
line++;
}
if(line == caretPosition.line) {
if(pos == caretPosition.pos)
break;
pos++;
}
}
return bytes;
}

TextPosition byteOffsetToCaret(dstring content, int byteOffset) {
int bytes = 0;
int line = 0;
int pos = 0;
TextPosition textPos;
foreach(c; content) {
if(bytes == byteOffset) {
//We all good.
textPos.line = line;
textPos.pos = pos;
return textPos;
}
bytes++;
if(c == '\n')
{
line++;
pos = 0;
}
else {
pos++;
}
}
return textPos;
}
}
6 changes: 5 additions & 1 deletion src/dlangide/ui/commands.d
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ enum IDEActions : int {
ProjectFolderRemoveItem,
ProjectFolderOpenItem,
ProjectFolderRenameItem,
GoToDefinition,
GetCompletionSuggestions,
InsertCompletion,
}

__gshared static this() {
Expand Down Expand Up @@ -92,4 +95,5 @@ const Action ACTION_HELP_ABOUT = new Action(IDEActions.HelpAbout, "MENU_HELP_ABO
const Action ACTION_WINDOW_CLOSE_ALL_DOCUMENTS = new Action(IDEActions.WindowCloseAllDocuments, "MENU_WINDOW_CLOSE_ALL_DOCUMENTS"c);
const Action ACTION_CREATE_NEW_WORKSPACE = new Action(IDEActions.CreateNewWorkspace, "Create new workspace"d);
const Action ACTION_ADD_TO_CURRENT_WORKSPACE = new Action(IDEActions.AddToCurrentWorkspace, "Add to current workspace"d);

const Action ACTION_GO_TO_DEFINITION = new Action(IDEActions.GoToDefinition, "GO_TO_DEFINITION"c, ""c, KeyCode.KEY_G, KeyFlag.Control);
const Action ACTION_GET_COMPLETIONS = new Action(IDEActions.GetCompletionSuggestions, "GO_TO_DEFINITION"c, ""c, KeyCode.KEY_G, KeyFlag.Control|KeyFlag.Shift);
28 changes: 27 additions & 1 deletion src/dlangide/ui/dsourceedit.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dlangui.core.logger;
import dlangui.widgets.editors;
import dlangui.widgets.srcedit;
import dlangui.widgets.menu;
import dlangui.widgets.popup;

import ddc.lexer.textsource;
import ddc.lexer.exceptions;
Expand Down Expand Up @@ -79,13 +80,38 @@ class DSourceEdit : SourceEdit {
case IDEActions.FileSave:
save();
return true;
case IDEActions.InsertCompletion:
EditOperation edit = new EditOperation(EditAction.Replace, getCaretPosition, a.label);
_content.performOperation(edit, this);
return true;
default:
break;
}
}
return super.handleAction(a);
}



void showCompletionPopup(dstring[] suggestions) {
MenuItem completionPopupItem = new MenuItem(null);
//Create popup menu
foreach(int i, dstring suggestion ; suggestions) {
auto action = new Action(IDEActions.InsertCompletion, suggestion);
completionPopupItem.add(action);
}
completionPopupItem.updateActionState(this);
PopupMenu popupMenu = new PopupMenu(completionPopupItem);
popupMenu.onMenuItemActionListener = this;
PopupWidget popup = window.showPopup(popupMenu, this, PopupAlign.Point | PopupAlign.Right, textPosToClient(_caretPos).left + left + _leftPaneWidth, textPosToClient(_caretPos).top + top + margins.top);
popup.flags = PopupFlags.CloseOnClickOutside;
Log.d("Showing popup at ", textPosToClient(_caretPos).left, " ", textPosToClient(_caretPos).top);
}

TextPosition getCaretPosition() {
return _caretPos;
}

/// change caret position and ensure it is visible
void setCaretPos(int line, int column)
{
Expand Down Expand Up @@ -245,7 +271,7 @@ class SimpleDSyntaxHighlighter : SyntaxHighlighter {
for (;;) {
ch = nextBracket(dir, p);
if (!ch) // no more brackets
return startPos;
break;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This caused a compiler issue for me at the last return in the function: "Statement is not reachable".

auto match = _bracketStack.process(ch);
if (match == BracketMatch.FOUND)
return p;
Expand Down
17 changes: 17 additions & 0 deletions src/dlangide/ui/frame.d
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import dlangui.widgets.appframe;
import dlangui.widgets.docks;
import dlangui.widgets.toolbars;
import dlangui.widgets.combobox;
import dlangui.widgets.popup;
import dlangui.dialogs.dialog;
import dlangui.dialogs.filedlg;
import dlangui.core.stdaction;
Expand All @@ -22,6 +23,7 @@ import dlangide.ui.homescreen;
import dlangide.workspace.workspace;
import dlangide.workspace.project;
import dlangide.builders.builder;
import dlangide.tools.editorTool;

import std.conv;
import std.utf;
Expand Down Expand Up @@ -60,6 +62,7 @@ class IDEFrame : AppFrame {
OutputPanel _logPanel;
DockHost _dockHost;
TabWidget _tabs;
EditorTool _editorTool;

dstring frameWindowCaptionSuffix = "DLangIDE"d;

Expand All @@ -71,6 +74,7 @@ class IDEFrame : AppFrame {
}

override protected void init() {
_editorTool = new DEditorTool(this);
super.init();
}

Expand Down Expand Up @@ -332,6 +336,9 @@ class IDEFrame : AppFrame {

editItem.add(ACTION_EDIT_PREFERENCES);

MenuItem navItem = new MenuItem(new Action(21, "MENU_NAVIGATE"));
navItem.add(ACTION_GO_TO_DEFINITION, ACTION_GET_COMPLETIONS);

MenuItem projectItem = new MenuItem(new Action(21, "MENU_PROJECT"));
projectItem.add(ACTION_PROJECT_SET_STARTUP, ACTION_PROJECT_REFRESH, ACTION_PROJECT_UPDATE_DEPENDENCIES, ACTION_PROJECT_SETTINGS);

Expand All @@ -353,6 +360,7 @@ class IDEFrame : AppFrame {
mainMenuItems.add(fileItem);
mainMenuItems.add(editItem);
mainMenuItems.add(projectItem);
mainMenuItems.add(navItem);
mainMenuItems.add(buildItem);
mainMenuItems.add(debugItem);
//mainMenuItems.add(viewItem);
Expand Down Expand Up @@ -513,6 +521,15 @@ class IDEFrame : AppFrame {
};
dlg.show();
return true;
case IDEActions.GoToDefinition:
Log.d("Trying to go to definition.");
_editorTool.goToDefinition(currentEditor(), currentEditor.getCaretPosition());
return true;
case IDEActions.GetCompletionSuggestions:
Log.d("Getting auto completion suggestions.");
auto results = _editorTool.getCompletions(currentEditor, currentEditor.getCaretPosition);
currentEditor.showCompletionPopup(results);
return true;
default:
return super.handleAction(a);
}
Expand Down
2 changes: 2 additions & 0 deletions views/res/i18n/en.ini
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ MENU_WINDOW_CLOSE_ALL_DOCUMENTS=Close All Documents
MENU_HELP=&HELP
MENU_HELP_VIEW_HELP=&View help
MENU_HELP_ABOUT=&About
GO_TO_DEFINITION=Go To Definition
MENU_NAVIGATE=NAVIGATE

TAB_LONG_LIST=Long list
TAB_BUTTONS=Buttons
Expand Down