home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / complete.C < prev    next >
C/C++ Source or Header  |  1998-10-23  |  12KB  |  503 lines

  1. // $Id: complete.C,v 1.23 1998/10/23 23:17:19 zeller Exp $ -*- C++ -*-
  2. // Command and arg completion
  3.  
  4. // Copyright (C) 1996-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char complete_rcsid[] = 
  30.     "$Id: complete.C,v 1.23 1998/10/23 23:17:19 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "complete.h"
  37.  
  38. #include "AppData.h"
  39. #include "Delay.h"
  40. #include "SmartC.h"
  41. #include "ddd.h"
  42. #include "disp-read.h"
  43. #include "editing.h"
  44. #include "isid.h"
  45. #include "post.h"
  46. #include "regexps.h"
  47. #include "string-fun.h"
  48.  
  49. #include <ctype.h>
  50.  
  51. #include <Xm/Xm.h>
  52. #include <Xm/Text.h>
  53. #include <Xm/TextF.h>
  54.  
  55.  
  56. // Completion delay flag
  57. static Delay *completion_delay = 0;
  58.  
  59. //-----------------------------------------------------------------------------
  60. // Line Completion
  61. //-----------------------------------------------------------------------------
  62.  
  63. // Remove adjacent duplicates in A
  64. static void uniq(string *a, int& size)
  65. {
  66.     int j = 1;
  67.     for (int i = 1; i < size; i++)
  68.     if (a[i] != a[j - 1])
  69.         a[j++] = a[i];
  70.     
  71.     size = j;
  72. }
  73.  
  74. // Info passed to reply functions
  75. struct CompletionInfo {
  76.     Widget widget;        // Widget
  77.     XEvent *event;        // Event 
  78.     string input;        // Current input
  79.     string cmd;            // Current command
  80.     string prefix;        // Current prefix
  81.  
  82.     CompletionInfo()
  83.     : widget(0), event(0), input(), cmd(), prefix()
  84.     {}
  85. private:
  86.     CompletionInfo(const CompletionInfo&)
  87.     : widget(0), event(0), input(), cmd(), prefix()
  88.     {
  89.     assert(0);
  90.     }
  91.  
  92.     CompletionInfo& operator = (const CompletionInfo&)
  93.     {
  94.     assert(0); return *this;
  95.     }
  96. };
  97.  
  98. static void complete_reply(const string& complete_answer, void *qu_data);
  99.  
  100. // Set completion
  101. static void set_completion(const CompletionInfo& info, string completion)
  102. {
  103.     // Set input to common prefix
  104.     if (info.widget == gdb_w)
  105.     {
  106.     private_gdb_output = true;
  107.  
  108.     XmTextReplace(gdb_w, promptPosition,
  109.               XmTextGetLastPosition(gdb_w), 
  110.               (String)completion);
  111.  
  112.     private_gdb_output = false;
  113.     }
  114.     else
  115.     {
  116.     if (XmIsTextField(info.widget))
  117.     {
  118.         XmTextFieldSetString(info.widget, (String)completion);
  119.     }
  120.     else if (XmIsText(info.widget))
  121.     {
  122.         XmTextSetString(info.widget, (String)completion);
  123.     }
  124.     }
  125. }
  126.  
  127. // Same, but for single possible completion
  128. static string complete_single_completion(string completion)
  129. {
  130.     // Only one possible expansion: Add final single quote if
  131.     // necessary and add final space as well.
  132.  
  133.     if (gdb->has_quotes())
  134.     {
  135.     if (completion.freq('\'') % 2 != 0)
  136.         completion += '\'';
  137.     }
  138.     completion += ' ';
  139.  
  140.     return completion;
  141. }
  142.  
  143.  
  144. // All completions are done
  145. static void completion_done(const CompletionInfo& info)
  146. {
  147.     if (XmIsTextField(info.widget))
  148.     {
  149.     XmTextPosition last_pos = 
  150.         XmTextFieldGetLastPosition(info.widget);
  151.     XmTextFieldSetInsertionPosition(info.widget, last_pos);
  152.     XmTextFieldShowPosition(info.widget, last_pos);
  153.     XmTextFieldSetEditable(info.widget, true);
  154.     }
  155.     else if (XmIsText(info.widget))
  156.     {
  157.     XmTextPosition last_pos = 
  158.         XmTextGetLastPosition(info.widget);
  159.     XmTextSetInsertionPosition(info.widget, last_pos);
  160.     XmTextShowPosition(info.widget, last_pos);
  161.     XmTextSetEditable(info.widget, true);
  162.     }
  163.  
  164.     XmTextSetEditable(gdb_w, true);
  165. }
  166.  
  167. void clear_completion_delay()
  168. {
  169.     if (completion_delay)
  170.     delete completion_delay;
  171.     completion_delay = 0;
  172. }
  173.  
  174. static string *completions  = 0;
  175. static int completions_size = 0;
  176.  
  177. // Send completion question
  178. static void complete(Widget w, XEvent *e, string input, string cmd)
  179. {
  180.     // Issue diagnostic if completion doesn't work right now
  181.     if (!gdb->isReadyWithPrompt())
  182.     {
  183.     post_gdb_busy(w);
  184.     return;
  185.     }
  186.  
  187.     static CompletionInfo info;
  188.     info.widget = w;
  189.     info.event  = e;
  190.     info.input  = input;
  191.     info.cmd    = cmd;
  192.     info.prefix = "";
  193.  
  194.     // Compare with last completions
  195.     static Widget last_completion_w     = 0;
  196.     static int    last_completion_index = -1;
  197.     static string last_completion       = string(char(-1));
  198.  
  199.     if (completions_size > 0 
  200.     && completions
  201.     && completions[0] != ""
  202.     && w == last_completion_w)
  203.     {
  204.     // Check if this is to be the next completion
  205.     string next_completion = "";
  206.  
  207.     if (input == last_completion)
  208.     {
  209.         // We have already shown possible completions:
  210.         // Expand to first completion
  211.         last_completion_index = 0;
  212.         next_completion = complete_single_completion(completions[0]);
  213.     }
  214.     else if (completions_size > 1
  215.          && last_completion_index >= 0 
  216.          && input == complete_single_completion(
  217.              completions[last_completion_index]))
  218.     {
  219.         if (last_completion_index < completions_size - 1)
  220.         {
  221.         // Show next completion
  222.         next_completion = complete_single_completion(
  223.             completions[++last_completion_index]);
  224.         }
  225.         else
  226.         {
  227.         // All completions shown; re-start with initial input
  228.         last_completion_index = -1;
  229.         next_completion = last_completion;
  230.         }
  231.     }
  232.  
  233.     if (next_completion != "")
  234.     {
  235.         set_completion(info, next_completion);
  236.         completion_done(info);
  237.         return;
  238.     }
  239.     }
  240.  
  241.     // Start a new completion session
  242.  
  243.     // Clear old completions
  244.     last_completion_w     = w;
  245.     last_completion       = input;
  246.     last_completion_index = -1;
  247.  
  248.     if (completions)
  249.     {
  250.     delete[] completions;
  251.     completions = 0;
  252.     }
  253.     completions_size = 0;
  254.  
  255.     // Go and ask GDB for completions.
  256.  
  257.     if (is_graph_cmd(cmd))
  258.     {
  259.     // Allow completion of `graph' commands
  260.     info.prefix = cmd.through(rxwhite);
  261.     cmd = cmd.from(int(info.prefix.length()));
  262.     }
  263.  
  264.     string complete_cmd;
  265.     if (gdb->type() == GDB)
  266.     {
  267.     complete_cmd = "complete " + cmd;
  268.     }
  269.     else if (gdb->type() == PERL)
  270.     {
  271.     string arg = cmd.after(rxwhite);
  272.     complete_cmd = "S ^" + arg + ".*";
  273.  
  274.     if (cmd == input)
  275.         info.prefix += cmd.through(rxwhite);
  276.     else
  277.         info.cmd = info.input;
  278.     }
  279.     else
  280.     assert(0);
  281.  
  282.     if (XmIsTextField(w))
  283.     XmTextFieldSetEditable(w, false);
  284.     else if (XmIsText(w))
  285.     XmTextSetEditable(w, false);
  286.     XmTextSetEditable(gdb_w, false);
  287.     
  288.     gdb->send_question(complete_cmd, complete_reply, (void *)&info);
  289.  
  290.     completion_delay = new Delay;
  291. }
  292.  
  293.  
  294. // Handle possible completions
  295. static void complete_reply(const string& complete_answer, void *qu_data)
  296. {
  297.     const CompletionInfo& info = *((CompletionInfo *)qu_data);
  298.  
  299.     bool from_gdb_w = (info.widget == gdb_w);
  300.  
  301.     string input = info.input;
  302.     strip_space(input);
  303.  
  304.     assert(completions == 0);
  305.  
  306.     int lines = complete_answer.freq('\n') + 1;
  307.     completions      = new string[lines];
  308.     completions_size = split(complete_answer, completions, lines, '\n');
  309.     smart_sort(completions, completions_size);
  310.     uniq(completions, completions_size);
  311.  
  312.     if (completions_size == 0 || completions[0] == "")
  313.     {
  314.     // No completion (sigh)
  315.     XtCallActionProc(gdb_w, "beep", info.event, 0, 0);
  316.     }
  317.     else if (completions[0].index("Undefined command:") == 0)
  318.     {
  319.     // GDB versions earlier than 4.13 do not support
  320.     // the `complete' command.
  321.  
  322.     string message;
  323.     for (int i = 0; i < completions_size; i++)
  324.         message += completions[i] + '\n';
  325.     post_error(message, "no_completion_error", info.widget);
  326.     }
  327.     else
  328.     {
  329.     if (info.cmd != info.input)
  330.     {
  331.         // Strip initial base command
  332.         for (int i = 0; i < completions_size; i++)
  333.         completions[i] = completions[i].after(' ');
  334.     }
  335.  
  336.     if (info.prefix != "")
  337.     {
  338.         // Add prefix again
  339.         for (int i = 0; i < completions_size; i++)
  340.         completions[i].prepend(info.prefix);
  341.     }
  342.  
  343.     // Find common prefix
  344.     string common_pfx = completions[0];
  345.     int i;
  346.     for (i = 1; i < completions_size; i++)
  347.         common_pfx = common_prefix(common_pfx, completions[i]);
  348.  
  349.     if (completions_size > 1 && input == common_pfx)
  350.     {
  351.         // We're already as far as we can get
  352.         if (from_gdb_w)
  353.         {
  354.         // Show possible expansions in command window.
  355.         int skip = common_pfx.index(rxwhite, -1) + 1;
  356.         
  357.         string insertion;
  358.         if (from_gdb_w)
  359.             insertion += input;
  360.         insertion += "\n";
  361.         for (i = 0; i < completions_size; i++)
  362.         {
  363.             insertion += completions[i].from(skip);
  364.             insertion += "\n";
  365.         }
  366.         gdb_out(insertion);
  367.         gdb_out(gdb->prompt());
  368.  
  369.         XmTextSetInsertionPosition(gdb_w, 
  370.                        XmTextGetLastPosition(gdb_w));
  371.         }
  372.     }
  373.     else
  374.     {
  375.         string completion = common_pfx;
  376.         if (completions_size == 1)
  377.         completion = complete_single_completion(completion);
  378.         set_completion(info, completion);
  379.     }
  380.     }
  381.  
  382.     completion_done(info);
  383. }
  384.  
  385. static void tabAct(Widget w, XEvent *e, String* args, Cardinal* num_args)
  386. {
  387.     if (XmIsText(w))
  388.     XtCallActionProc(w, "process-tab", e, args, *num_args);
  389.     else if (XmIsPrimitive(w))
  390.     XtCallActionProc(w, "PrimitiveNextTabGroup", e, args, *num_args);
  391. }
  392.  
  393. // Complete current GDB command
  394. void complete_commandAct(Widget w, XEvent *e, String* args, Cardinal* num_args)
  395. {
  396.     if ((gdb->type() != GDB && gdb->type() != PERL)
  397.     || w != gdb_w
  398.     || !gdb->isReadyWithPrompt()
  399.     || XmTextGetInsertionPosition(w) != XmTextGetLastPosition(w))
  400.     {
  401.     tabAct(w, e, args, num_args);
  402.     return;
  403.     }
  404.  
  405.     string input = current_line();
  406.     if (gdb->has_quotes())
  407.     {
  408.     // Insert single quote if necessary
  409.     if (is_break_cmd(input))
  410.     {
  411.         int last_space = input.index(rxwhite, -1);
  412.         if (last_space >= 0)
  413.         {
  414.         string last_word = input.after(last_space);
  415.         if (last_word.length() > 0 && last_word[0] != '\'')
  416.         {
  417.             for (int i = 0; i < int(last_word.length()); i++)
  418.             {
  419.             char c = last_word[i];
  420.             if (!isid(c))
  421.             {
  422.                 input(last_space + 1, 0) = '\'';
  423.                 break;
  424.             }
  425.             }
  426.         }
  427.         }
  428.     }
  429.     }
  430.  
  431.     complete(w, e, input, input);
  432. }
  433.  
  434.  
  435. // Complete GDB argument
  436. static void _complete_argAct(Widget w, 
  437.                  XEvent *e, 
  438.                  String* args, 
  439.                  Cardinal* num_args,
  440.                  bool tab)
  441. {
  442.     if ((tab && !app_data.global_tab_completion) 
  443.     || (gdb->type() != GDB && gdb->type() != PERL)
  444.     || !gdb->isReadyWithPrompt())
  445.     {
  446.     tabAct(w, e, args, num_args);
  447.     return;
  448.     }
  449.  
  450.     // The command to use as prefix for completions
  451.     string base = gdb->print_command("");
  452.     if (*num_args >= 1)
  453.     base = args[0];
  454.     strip_space(base);
  455.  
  456.     String _input = 0;
  457.     if (XmIsTextField(w))
  458.     _input = XmTextFieldGetString(w);
  459.     else if (XmIsText(w))
  460.     _input = XmTextGetString(w);
  461.  
  462.     if (_input == 0)
  463.     return;
  464.  
  465.     string input(_input);
  466.     XtFree(_input);
  467.  
  468.     if (gdb->has_quotes())
  469.     {
  470.     // Insert single quote if necessary
  471.     if (is_break_cmd(base))
  472.     {
  473.         if (input.length() > 0 && input[0] != '\'')
  474.         {
  475.         for (int i = 0; i < int(input.length()); i++)
  476.         {
  477.             char c = input[i];
  478.             if (!isid(c))
  479.             {
  480.             input(0, 0) = '\'';
  481.             break;
  482.             }
  483.         }
  484.         }
  485.     }
  486.     }
  487.  
  488.     if (base != "")
  489.     complete(w, e, input, base + " " + input);
  490.     else
  491.     complete(w, e, input, input);
  492. }
  493.  
  494. void complete_argAct(Widget w, XEvent *e, String* args, Cardinal* num_args)
  495. {
  496.     _complete_argAct(w, e, args, num_args, false);
  497. }
  498.  
  499. void complete_tabAct(Widget w, XEvent *e, String* args, Cardinal* num_args)
  500. {
  501.     _complete_argAct(w, e, args, num_args, true);
  502. }
  503.