From b4b0b4cc3314f54f705fb582a25424453a9c7627 Mon Sep 17 00:00:00 2001 From: MaxED Date: Tue, 13 May 2014 14:41:51 +0000 Subject: [PATCH] Script Editor: added Visual Studio-like snippet expanding (type the name of a snippet, then press the Tab key to expand it). Script Editor: added snippets to auto-complete list (currently they aren't inserted properly though...). Script Editor: auto-indentation now works a bit smarter. ScintillaControl.GetLine() was retrieving incorrect line. --- Source/Core/Builder.csproj | 1 + Source/Core/Controls/ScintillaControl.cs | 2 +- Source/Core/Controls/ScriptDocumentTab.cs | 6 +- Source/Core/Controls/ScriptEditorControl.cs | 154 ++++++++++++------- Source/Core/Properties/Resources.Designer.cs | 7 + Source/Core/Properties/Resources.resx | 3 + Source/Core/Resources/Lexers.cfg | 3 + Source/Core/Resources/ScriptSnippet.xpm | 86 +++++++++++ 8 files changed, 206 insertions(+), 56 deletions(-) create mode 100644 Source/Core/Resources/ScriptSnippet.xpm diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index ea4cd6de..5ac9cbab 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -671,6 +671,7 @@ + diff --git a/Source/Core/Controls/ScintillaControl.cs b/Source/Core/Controls/ScintillaControl.cs index e566ea17..27ed995f 100644 --- a/Source/Core/Controls/ScintillaControl.cs +++ b/Source/Core/Controls/ScintillaControl.cs @@ -3127,7 +3127,7 @@ namespace CodeImp.DoomBuilder.Controls byte[] buffer = new byte[sz + 1]; fixed(byte* b = buffer) - FastPerform(2153, (uint)line + 1, (uint)b); + FastPerform(2153, (uint)line, (uint)b); return System.Text.UTF8Encoding.UTF8.GetString(buffer, 0, sz); } diff --git a/Source/Core/Controls/ScriptDocumentTab.cs b/Source/Core/Controls/ScriptDocumentTab.cs index 211c3075..bdc0ca37 100644 --- a/Source/Core/Controls/ScriptDocumentTab.cs +++ b/Source/Core/Controls/ScriptDocumentTab.cs @@ -48,15 +48,15 @@ namespace CodeImp.DoomBuilder.Controls #region ================== Variables // The script edit control - protected ScriptEditorControl editor; + protected readonly ScriptEditorControl editor; //mxd - protected ComboBox navigator; + protected readonly ComboBox navigator; // Derived classes must set this! protected ScriptConfiguration config; // The panel we're on - protected ScriptEditorPanel panel; + protected readonly ScriptEditorPanel panel; #endregion diff --git a/Source/Core/Controls/ScriptEditorControl.cs b/Source/Core/Controls/ScriptEditorControl.cs index a1494c9b..489d17b0 100644 --- a/Source/Core/Controls/ScriptEditorControl.cs +++ b/Source/Core/Controls/ScriptEditorControl.cs @@ -41,11 +41,12 @@ namespace CodeImp.DoomBuilder.Controls private const int MAX_BACKTRACK_LENGTH = 200; // Index for registered images - private enum ImageIndex : int + private enum ImageIndex { ScriptConstant = 0, ScriptKeyword = 1, - ScriptError = 2 + ScriptError = 2, + ScriptSnippet = 3, //mxd } #endregion @@ -154,6 +155,7 @@ namespace CodeImp.DoomBuilder.Controls // Images RegisterAutoCompleteImage(ImageIndex.ScriptConstant, Resources.ScriptConstant); RegisterAutoCompleteImage(ImageIndex.ScriptKeyword, Resources.ScriptKeyword); + RegisterAutoCompleteImage(ImageIndex.ScriptSnippet, Resources.ScriptSnippet); //mxd RegisterMarkerImage(ImageIndex.ScriptError, Resources.ScriptError); // Events @@ -244,8 +246,7 @@ namespace CodeImp.DoomBuilder.Controls Stream lexersdata; StreamReader lexersreader; Configuration lexercfg = new Configuration(); - int imageindex; - + // Make collections stylelookup = new Dictionary(); SortedList autocompletelist = new SortedList(StringComparer.Ordinal); @@ -347,7 +348,7 @@ namespace CodeImp.DoomBuilder.Controls } // Create the keywords list and apply it - imageindex = (int)ImageIndex.ScriptKeyword; + string imageindex = ((int)ImageIndex.ScriptKeyword).ToString(CultureInfo.InvariantCulture); int keywordsindex = lexercfg.ReadSetting(lexername + ".keywordsindex", -1); if(keywordsindex > -1) { @@ -356,7 +357,7 @@ namespace CodeImp.DoomBuilder.Controls { if(keywordslist.Length > 0) keywordslist.Append(" "); keywordslist.Append(k); - autocompletelist.Add(k.ToUpperInvariant(), k + "?" + imageindex.ToString(CultureInfo.InvariantCulture)); + autocompletelist.Add(k.ToUpperInvariant(), k + "?" + imageindex); } string words = keywordslist.ToString(); if(scriptconfig.CaseSensitive) @@ -366,7 +367,7 @@ namespace CodeImp.DoomBuilder.Controls } // Create the constants list and apply it - imageindex = (int)ImageIndex.ScriptConstant; + imageindex = ((int)ImageIndex.ScriptConstant).ToString(CultureInfo.InvariantCulture); int constantsindex = lexercfg.ReadSetting(lexername + ".constantsindex", -1); if(constantsindex > -1) { @@ -375,7 +376,7 @@ namespace CodeImp.DoomBuilder.Controls { if(constantslist.Length > 0) constantslist.Append(" "); constantslist.Append(c); - autocompletelist.Add(c.ToUpperInvariant(), c + "?" + imageindex.ToString(CultureInfo.InvariantCulture)); + autocompletelist.Add(c.ToUpperInvariant(), c + "?" + imageindex); } string words = constantslist.ToString(); if(scriptconfig.CaseSensitive) @@ -383,6 +384,26 @@ namespace CodeImp.DoomBuilder.Controls else scriptedit.KeyWords(constantsindex, words.ToLowerInvariant()); } + + //mxd. Create the snippets list and apply it + imageindex = ((int)ImageIndex.ScriptSnippet).ToString(CultureInfo.InvariantCulture); + int snippetindex = lexercfg.ReadSetting(lexername + ".snippetindex", -1); + if(snippetindex > -1 && scriptconfig.Snippets.Count > 0) + { + StringBuilder snippetslist = new StringBuilder(""); + foreach(string c in scriptconfig.Snippets.Keys) + { + if(autocompletelist.ContainsKey(c.ToUpperInvariant())) continue; + if(snippetslist.Length > 0) snippetslist.Append(" "); + snippetslist.Append(c); + autocompletelist.Add(c.ToUpperInvariant(), c + "?" + imageindex); + } + string words = snippetslist.ToString(); + if(scriptconfig.CaseSensitive) + scriptedit.KeyWords(snippetindex, words); + else + scriptedit.KeyWords(snippetindex, words.ToLowerInvariant()); + } // Sort the autocomplete list List autocompleteplainlist = new List(autocompletelist.Values); @@ -640,24 +661,36 @@ namespace CodeImp.DoomBuilder.Controls int numtabs = scriptedit.GetLineIndentation(curline); string tabs = Environment.NewLine + new String(' ', numtabs); string spaces = new String(' ', General.Settings.ScriptTabWidth); - for (int i = 0; i < lines.Length; i++) { - lines[i] = lines[i].Replace("\t", spaces); - } - string text = string.Join(tabs, lines); - scriptedit.InsertText(scriptedit.SelectionStart, text); + int entrypos = -1; + int entryline = -1; + string[] processedlines = new string[lines.Length]; - //check if we have the $EP marker - for(int i = 0; i < lines.Length; i++) { - int pos = lines[i].IndexOf("$EP"); - if(pos != -1) { - MoveToLine(curline + i); - pos += scriptedit.PositionFromLine(curline + i); - scriptedit.SelectionStart = pos + numtabs; - scriptedit.SelectionEnd = pos + numtabs + 3; - ReplaceSelection(""); - break; + for (int i = 0; i < lines.Length; i++) { + processedlines[i] = lines[i].Replace("\t", spaces); + + //check if we have the $EP marker + if (entrypos == -1) { + int pos = processedlines[i].IndexOf("$EP"); + if (pos != -1) { + entryline = curline + i; + entrypos = pos + numtabs; + processedlines[i] = processedlines[i].Remove(pos, 3); + } } } + + //replace the text + string text = string.Join(tabs, processedlines); + scriptedit.SelectionStart = scriptedit.WordStartPosition(scriptedit.CurrentPos, true); + scriptedit.SelectionEnd = scriptedit.WordEndPosition(scriptedit.CurrentPos, true); + scriptedit.ReplaceSel(text); + + //move the cursor if we had the $EP marker + if (entrypos != -1) { + MoveToLine(entryline); + scriptedit.SelectionStart = scriptedit.PositionFromLine(entryline) + entrypos; + scriptedit.SelectionEnd = scriptedit.PositionFromLine(entryline) + entrypos; + } } #endregion @@ -693,65 +726,65 @@ namespace CodeImp.DoomBuilder.Controls { // These key combinations put odd characters in the script, so I disabled them if((e.KeyCode == Keys.Q) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.W) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.E) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.R) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.Y) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.U) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.I) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.P) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.A) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; - if((e.KeyCode == Keys.D) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.G) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.H) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.J) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.K) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.L) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.Z) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; - if((e.KeyCode == Keys.X) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; - if((e.KeyCode == Keys.C) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; - if((e.KeyCode == Keys.V) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; - if((e.KeyCode == Keys.B) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.N) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; - if((e.KeyCode == Keys.M) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.W) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.E) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.R) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.Y) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.U) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.I) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.P) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.A) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + else if((e.KeyCode == Keys.D) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.G) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.H) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.J) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.K) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.L) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.Z) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + else if((e.KeyCode == Keys.X) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + else if((e.KeyCode == Keys.C) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + else if((e.KeyCode == Keys.V) && ((e.Modifiers & Keys.Control) == Keys.Control) && ((e.Modifiers & Keys.Shift) == Keys.Shift)) e.Handled = true; + else if((e.KeyCode == Keys.B) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.N) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; + else if((e.KeyCode == Keys.M) && ((e.Modifiers & Keys.Control) == Keys.Control)) e.Handled = true; // F3 for Find Next - if((e.KeyCode == Keys.F3) && (e.Modifiers == Keys.None)) + else if((e.KeyCode == Keys.F3) && (e.Modifiers == Keys.None)) { if(OnFindNext != null) OnFindNext(); e.Handled = true; } // F2 for Keyword Help - if((e.KeyCode == Keys.F2) && (e.Modifiers == Keys.None)) + else if((e.KeyCode == Keys.F2) && (e.Modifiers == Keys.None)) { LaunchKeywordHelp(); e.Handled = true; } // CTRL+F for find & replace - if((e.KeyCode == Keys.F) && ((e.Modifiers & Keys.Control) == Keys.Control)) + else if((e.KeyCode == Keys.F) && ((e.Modifiers & Keys.Control) == Keys.Control)) { if(OnOpenFindAndReplace != null) OnOpenFindAndReplace(); e.Handled = true; } // CTRL+S for save - if((e.KeyCode == Keys.S) && ((e.Modifiers & Keys.Control) == Keys.Control)) + else if((e.KeyCode == Keys.S) && ((e.Modifiers & Keys.Control) == Keys.Control)) { if(OnExplicitSaveTab != null) OnExplicitSaveTab(); e.Handled = true; } // CTRL+O for open - if((e.KeyCode == Keys.O) && ((e.Modifiers & Keys.Control) == Keys.Control)) + else if((e.KeyCode == Keys.O) && ((e.Modifiers & Keys.Control) == Keys.Control)) { if(OnOpenScriptBrowser != null) OnOpenScriptBrowser(); e.Handled = true; } // CTRL+Space to autocomplete - if((e.KeyCode == Keys.Space) && (e.Modifiers == Keys.Control)) + else if((e.KeyCode == Keys.Space) && (e.Modifiers == Keys.Control)) { // Hide call tip if any scriptedit.CallTipCancel(); @@ -763,6 +796,17 @@ namespace CodeImp.DoomBuilder.Controls e.Handled = true; } + + //mxd. Tab to expand code snippet + else if(e.KeyCode == Keys.Tab) + { + string curword = GetCurrentWord().ToLowerInvariant(); + if (scriptconfig.Snippets.ContainsKey(curword)) + { + InsertSnippet(scriptconfig.Snippets[curword]); + e.Handled = true; + } + } } // Key released @@ -784,11 +828,17 @@ namespace CodeImp.DoomBuilder.Controls { // Apply identation of the previous line to this line int ident = scriptedit.GetLineIndentation(curline - 1); - int tabs = ident ;// / scriptedit.Indent; + + //mxd. Indent a bit more if a code block is started on the previous line + string prevline = scriptedit.GetLine(curline - 1); + int blockstartpos = prevline.LastIndexOf("{"); //TODO: should we move block start/end symbols to script configuration?.. + int blockendpos = prevline.LastIndexOf("}"); + if(blockstartpos > blockendpos) ident += General.Settings.ScriptTabWidth; + if(scriptedit.GetLineIndentation(curline) == 0) { scriptedit.SetLineIndentation(curline, ident); - scriptedit.SetSel(scriptedit.SelectionStart + tabs, scriptedit.SelectionStart + tabs); + scriptedit.SetSel(scriptedit.SelectionStart + ident, scriptedit.SelectionStart + ident); } } } diff --git a/Source/Core/Properties/Resources.Designer.cs b/Source/Core/Properties/Resources.Designer.cs index 5dc14d47..082837d2 100644 --- a/Source/Core/Properties/Resources.Designer.cs +++ b/Source/Core/Properties/Resources.Designer.cs @@ -564,6 +564,13 @@ namespace CodeImp.DoomBuilder.Properties { } } + internal static byte[] ScriptSnippet { + get { + object obj = ResourceManager.GetObject("ScriptSnippet", resourceCulture); + return ((byte[])(obj)); + } + } + internal static System.Drawing.Bitmap Search { get { object obj = ResourceManager.GetObject("Search", resourceCulture); diff --git a/Source/Core/Properties/Resources.resx b/Source/Core/Properties/Resources.resx index 1be80854..c83df003 100644 --- a/Source/Core/Properties/Resources.resx +++ b/Source/Core/Properties/Resources.resx @@ -427,4 +427,7 @@ ..\Resources\PuzzlePiece.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\ScriptSnippet.xpm;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/Source/Core/Resources/Lexers.cfg b/Source/Core/Resources/Lexers.cfg index a9523310..a62b35c7 100644 --- a/Source/Core/Resources/Lexers.cfg +++ b/Source/Core/Resources/Lexers.cfg @@ -36,6 +36,7 @@ lexer3 // CPP-style, case-sensitive keywordsindex = 0; constantsindex = 1; + snippetindex = 2; } @@ -70,6 +71,7 @@ lexer18 // Pascal-style keywordsindex = 0; constantsindex = 1; + snippetindex = 2; } @@ -88,6 +90,7 @@ lexer35 // CPP-style, case-insensitive keywordsindex = 0; constantsindex = 1; + snippetindex = 2; } diff --git a/Source/Core/Resources/ScriptSnippet.xpm b/Source/Core/Resources/ScriptSnippet.xpm new file mode 100644 index 00000000..cca5cc19 --- /dev/null +++ b/Source/Core/Resources/ScriptSnippet.xpm @@ -0,0 +1,86 @@ +/* XPM */ +static char *PuzzlePiece___[] = { +/* columns rows colors chars-per-pixel */ +"16 16 64 1", +" c #3C8C38", +". c #41903C", +"X c #469541", +"o c #4A9945", +"O c #4E9D48", +"+ c #51A04B", +"@ c #52A14C", +"# c #55A44F", +"$ c #59B54F", +"% c #5AA853", +"& c #5EAC57", +"* c #5CB752", +"= c #62AE5C", +"- c #60B955", +"; c #66B55F", +": c #63BA58", +"> c #65BB59", +", c #66BB5D", +"< c #68BC5B", +"1 c #69BD5D", +"2 c #6CBE5F", +"3 c #6DB566", +"4 c #6AB862", +"5 c #6BBD62", +"6 c #6DBF60", +"7 c #6EBC65", +"8 c #6FC064", +"9 c #72C165", +"0 c #72C169", +"q c #76C26A", +"w c #77C46D", +"e c #78C66F", +"r c #7AC072", +"t c #7AC770", +"y c #7CC376", +"u c #7BC971", +"i c #7DC973", +"p c #7ECB74", +"a c #80CE76", +"s c #81C67B", +"d c #87CB79", +"f c #82CE79", +"g c #84C87D", +"h c #83D079", +"j c #87CA80", +"k c #88CA81", +"l c #89CA83", +"z c #8ACB83", +"x c #8BCC83", +"c c #8DCD85", +"v c #91CF89", +"b c #94CF8B", +"n c #96D18D", +"m c #99D290", +"M c #9CD493", +"N c #9ED595", +"B c #A1D797", +"V c #A2D897", +"C c #A2D798", +"Z c #A3D899", +"A c #A4D899", +"S c #A7D99C", +"D c #E6F5E4", +"F c None", +/* pixels */ +"FFFFFFFFFFFFFFFF", +"FFFFFfaiiFFFFFFF", +"FFFFDfSVwFFFFFFF", +"FFFFFiVnqFFFFFFF", +"FhapueBM74;=FFFF", +"FaSACBNMmnb&FFFF", +"FpZBdq9999b%FFFF", +"FtwgM9821>v3@OoF", +"FFF7m211>-8xkkXF", +"FFF4n1>>-*5kgg.F", +"F74rb>*-*$j=X..F", +"F4bv0cclk,gXFFFF", +"F;v-c3@O=gs.FFFF", +"F=cxl@FFXsy FFFF", +"F&%#@OFF.. FFFF", +"FFFFFFFFFFFFFFFF" +};