1 /*
2  * EditingPreferencesPane.java
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 package org.rstudio.studio.client.workbench.prefs.views;
16 
17 import com.google.gwt.dom.client.Style.Unit;
18 import com.google.gwt.event.dom.client.ChangeEvent;
19 import com.google.gwt.event.dom.client.ChangeHandler;
20 import com.google.gwt.event.dom.client.ClickEvent;
21 import com.google.gwt.event.dom.client.ClickHandler;
22 import com.google.gwt.event.logical.shared.ValueChangeEvent;
23 import com.google.gwt.event.logical.shared.ValueChangeHandler;
24 import com.google.gwt.resources.client.ImageResource;
25 import com.google.gwt.user.client.ui.CheckBox;
26 import com.google.gwt.user.client.ui.HorizontalPanel;
27 import com.google.gwt.user.client.ui.Label;
28 import com.google.gwt.user.client.ui.VerticalPanel;
29 import com.google.inject.Inject;
30 
31 import org.rstudio.core.client.ElementIds;
32 import org.rstudio.core.client.StringUtil;
33 import org.rstudio.core.client.command.KeyboardShortcut;
34 import org.rstudio.core.client.command.ShortcutManager;
35 import org.rstudio.core.client.prefs.PreferencesDialogBaseResources;
36 import org.rstudio.core.client.prefs.RestartRequirement;
37 import org.rstudio.core.client.resources.ImageResource2x;
38 import org.rstudio.core.client.theme.DialogTabLayoutPanel;
39 import org.rstudio.core.client.theme.VerticalTabPanel;
40 import org.rstudio.core.client.widget.HelpButton;
41 import org.rstudio.core.client.widget.ModifyKeyboardShortcutsWidget;
42 import org.rstudio.core.client.widget.NumericValueWidget;
43 import org.rstudio.core.client.widget.OperationWithInput;
44 import org.rstudio.core.client.widget.SelectWidget;
45 import org.rstudio.core.client.widget.SmallButton;
46 import org.rstudio.core.client.widget.TextBoxWithButton;
47 import org.rstudio.studio.client.common.DiagnosticsHelpLink;
48 import org.rstudio.studio.client.common.HelpLink;
49 import org.rstudio.studio.client.common.SimpleRequestCallback;
50 import org.rstudio.studio.client.workbench.prefs.model.UserPrefs;
51 import org.rstudio.studio.client.workbench.snippets.ui.EditSnippetsDialog;
52 import org.rstudio.studio.client.workbench.views.source.editors.text.IconvListResult;
53 import org.rstudio.studio.client.workbench.views.source.editors.text.ui.ChooseEncodingDialog;
54 import org.rstudio.studio.client.workbench.views.source.model.SourceServerOperations;
55 
56 public class EditingPreferencesPane extends PreferencesPane
57 {
58    @Inject
EditingPreferencesPane(UserPrefs prefs, SourceServerOperations server, PreferencesDialogResources res)59    public EditingPreferencesPane(UserPrefs prefs,
60                                  SourceServerOperations server,
61                                  PreferencesDialogResources res)
62    {
63       prefs_ = prefs;
64       server_ = server;
65       PreferencesDialogBaseResources baseRes = PreferencesDialogBaseResources.INSTANCE;
66 
67       VerticalTabPanel editingPanel = new VerticalTabPanel(ElementIds.EDIT_EDITING_PREFS);
68       editingPanel.add(headerLabel("General"));
69       editingPanel.add(tight(spacesForTab_ = checkboxPref("Insert spaces for tab", prefs.useSpacesForTab(),
70             false /*defaultSpace*/)));
71       editingPanel.add(indent(tabWidth_ = numericPref("Tab width", 1, UserPrefs.MAX_TAB_WIDTH,
72             prefs.numSpacesForTab())));
73       tabWidth_.setWidth("36px");
74       editingPanel.add(checkboxPref(
75             "Auto-detect code indentation",
76             prefs_.autoDetectIndentation(),
77             "When enabled, the indentation for documents not part of an RStudio project " +
78             "will be automatically detected."));
79       editingPanel.add(checkboxPref("Insert matching parens/quotes", prefs_.insertMatching()));
80       editingPanel.add(checkboxPref("Use native pipe operator, |> (requires R 4.1+)",
81          prefs_.insertNativePipeOperator()));
82       editingPanel.add(checkboxPref("Auto-indent code after paste", prefs_.reindentOnPaste()));
83       editingPanel.add(checkboxPref("Vertically align arguments in auto-indent", prefs_.verticallyAlignArgumentsIndent()));
84       editingPanel.add(checkboxPref("Soft-wrap R source files", prefs_.softWrapRFiles()));
85       editingPanel.add(checkboxPref(
86             "Continue comment when inserting new line",
87             prefs_.continueCommentsOnNewline(),
88             "When enabled, pressing Enter will continue comments on new lines. " +
89             "Press Shift + Enter to exit a comment."));
90       editingPanel.add(checkboxPref(
91             "Enable hyperlink highlighting in editor",
92             prefs_.highlightWebLink(),
93             "When enabled, hyperlinks in comments will be underlined and clickable. "));
94 
95       delimiterSurroundWidget_ = new SelectWidget(
96             "Surround selection on text insertion:",
97             new String[] {
98                   "Never",
99                   "Quotes",
100                   "Quotes & Brackets"
101             },
102             new String[] {
103                   UserPrefs.SURROUND_SELECTION_NEVER,
104                   UserPrefs.SURROUND_SELECTION_QUOTES,
105                   UserPrefs.SURROUND_SELECTION_QUOTES_AND_BRACKETS
106             },
107             false,
108             true,
109             false);
110       editingPanel.add(delimiterSurroundWidget_);
111 
112       HorizontalPanel keyboardPanel = new HorizontalPanel();
113       editorMode_ = new SelectWidget(
114             "Keybindings:",
115             new String[] {
116                   "Default",
117                   "Vim",
118                   "Emacs",
119                   "Sublime Text"
120             },
121             new String[] {
122                   UserPrefs.EDITOR_KEYBINDINGS_DEFAULT,
123                   UserPrefs.EDITOR_KEYBINDINGS_VIM,
124                   UserPrefs.EDITOR_KEYBINDINGS_EMACS,
125                   UserPrefs.EDITOR_KEYBINDINGS_SUBLIME,
126             },
127             false,
128             true,
129             false);
130       editorMode_.getElement().getStyle().setMarginBottom(0, Unit.PX);
131 
132       keyboardPanel.add(editorMode_);
133       SmallButton editShortcuts = new SmallButton("Modify Keyboard Shortcuts...");
134       editShortcuts.getElement().getStyle().setMarginLeft(15, Unit.PX);
135       editShortcuts.addClickHandler(new ClickHandler() {
136          @Override
137          public void onClick(ClickEvent event)
138          {
139             new ModifyKeyboardShortcutsWidget().showModal();
140          }
141       });
142       keyboardPanel.add(editShortcuts);
143 
144 
145       lessSpaced(keyboardPanel);
146       editingPanel.add(keyboardPanel);
147 
148       Label executionLabel = headerLabel("Execution");
149       editingPanel.add(executionLabel);
150       executionLabel.getElement().getStyle().setMarginTop(8, Unit.PX);
151       editingPanel.add(checkboxPref("Focus console after executing from source", prefs_.focusConsoleAfterExec()));
152 
153       executionBehavior_ = new SelectWidget(
154             "Ctrl+Enter executes: ",
155             new String[] {
156                "Current line",
157                "Multi-line R statement",
158                "Multiple consecutive R lines"
159             },
160             new String[] {
161                UserPrefs.EXECUTION_BEHAVIOR_LINE,
162                UserPrefs.EXECUTION_BEHAVIOR_STATEMENT,
163                UserPrefs.EXECUTION_BEHAVIOR_PARAGRAPH
164             },
165             false,
166             true,
167             false);
168       executionBehavior_.getElement().getStyle().setMarginBottom(0, Unit.PX);
169       editingPanel.add(executionBehavior_);
170 
171       Label snippetsLabel = headerLabel("Snippets");
172       snippetsLabel.getElement().getStyle().setMarginTop(8, Unit.PX);
173       editingPanel.add(snippetsLabel);
174 
175       HorizontalPanel panel = new HorizontalPanel();
176       CheckBox enableSnippets = checkboxPref("Enable code snippets", prefs_.enableSnippets());
177       panel.add(enableSnippets);
178 
179       SmallButton editSnippets = new SmallButton("Edit Snippets...");
180       editSnippets.getElement().getStyle().setMarginTop(1, Unit.PX);
181       editSnippets.getElement().getStyle().setMarginLeft(5, Unit.PX);
182       editSnippets.addClickHandler(new ClickHandler() {
183          @Override
184          public void onClick(ClickEvent event)
185          {
186             new EditSnippetsDialog().showModal();
187          }
188       });
189       panel.add(editSnippets);
190 
191       HelpButton snippetHelp = new HelpButton("code_snippets", "Help on code snippets");
192       snippetHelp.getElement().getStyle().setMarginTop(2, Unit.PX);
193       snippetHelp.getElement().getStyle().setMarginLeft(6, Unit.PX);
194       panel.add(snippetHelp);
195 
196       editingPanel.add(panel);
197 
198       VerticalTabPanel displayPanel = new VerticalTabPanel(ElementIds.EDIT_DISPLAY_PREFS);
199       displayPanel.add(headerLabel("General"));
200       displayPanel.add(checkboxPref("Highlight selected word", prefs.highlightSelectedWord()));
201       displayPanel.add(checkboxPref("Highlight selected line", prefs.highlightSelectedLine()));
202       displayPanel.add(checkboxPref("Show line numbers", prefs.showLineNumbers()));
203       displayPanel.add(tight(showMargin_ = checkboxPref("Show margin",
204             prefs.showMargin(), false /*defaultSpace*/)));
205       displayPanel.add(indent(marginCol_ = numericPref("Margin column", prefs.marginColumn())));
206       displayPanel.add(checkboxPref("Show whitespace characters", prefs_.showInvisibles()));
207       displayPanel.add(checkboxPref("Show indent guides", prefs_.showIndentGuides()));
208       displayPanel.add(checkboxPref("Blinking cursor", prefs_.blinkingCursor()));
209       displayPanel.add(checkboxPref("Allow scroll past end of document", prefs_.scrollPastEndOfDocument()));
210       displayPanel.add(checkboxPref("Allow drag and drop of text", prefs_.enableTextDrag()));
211       displayPanel.add(checkboxPref("Highlight R function calls", prefs_.highlightRFunctionCalls()));
212       displayPanel.add(extraSpaced(
213          checkboxPref("Rainbow parentheses", prefs_.rainbowParentheses(), false /* defaultSpace */)));
214 
215       foldMode_ = new SelectWidget(
216             "Fold Style:",
217             new String[] {
218                   "Start Only",
219                   "Start and End"
220             },
221             new String[] {
222                   UserPrefs.FOLD_STYLE_BEGIN_ONLY,
223                   UserPrefs.FOLD_STYLE_BEGIN_AND_END
224             },
225             false,
226             true,
227             false);
228 
229       displayPanel.add(foldMode_);
230 
231       VerticalTabPanel savePanel = new VerticalTabPanel(ElementIds.EDIT_SAVING_PREFS);
232 
233       savePanel.add(headerLabel("General"));
234       savePanel.add(checkboxPref("Ensure that source files end with newline", prefs_.autoAppendNewline()));
235       savePanel.add(checkboxPref("Strip trailing horizontal whitespace when saving", prefs_.stripTrailingWhitespace()));
236       savePanel.add(checkboxPref("Restore last cursor position when opening file", prefs_.restoreSourceDocumentCursorPosition()));
237 
238       Label serializationLabel = headerLabel("Serialization");
239       serializationLabel.getElement().getStyle().setPaddingTop(14, Unit.PX);
240       savePanel.add(serializationLabel);
241 
242 
243       lineEndings_ = new LineEndingsSelectWidget();
244       spaced(lineEndings_);
245       savePanel.add(lineEndings_);
246 
247       encodingValue_ = prefs_.defaultEncoding().getGlobalValue();
248       savePanel.add(lessSpaced(encoding_ = new TextBoxWithButton(
249             "Default text encoding:",
250             "",
251             "Change...",
252             null,
253             ElementIds.TextBoxButtonId.TEXT_ENCODING,
254             true,
255             new ClickHandler()
256             {
257                public void onClick(ClickEvent event)
258                {
259                   server_.iconvlist(new SimpleRequestCallback<IconvListResult>()
260                   {
261                      @Override
262                      public void onResponseReceived(IconvListResult response)
263                      {
264                         new ChooseEncodingDialog(
265                               response.getCommon(),
266                               response.getAll(),
267                               encodingValue_,
268                               true,
269                               false,
270                               new OperationWithInput<String>()
271                               {
272                                  public void execute(String encoding)
273                                  {
274                                     if (encoding == null)
275                                        return;
276 
277                                     setEncoding(encoding);
278                                  }
279                               }).showModal();
280                      }
281                   });
282 
283                }
284             })));
285       nudgeRight(encoding_);
286       textBoxWithChooser(encoding_);
287       spaced(encoding_);
288       setEncoding(prefs.defaultEncoding().getGlobalValue());
289 
290       savePanel.add(spacedBefore(headerLabel("Auto-save")));
291       savePanel.add(checkboxPref("Always save R scripts before sourcing", prefs.saveBeforeSourcing()));
292       savePanel.add(checkboxPref("Automatically save when editor loses focus", prefs_.autoSaveOnBlur()));
293       autoSaveOnIdle_ = new SelectWidget(
294             "When editor is idle: ",
295             new String[] {
296                "Backup unsaved changes",
297                "Save and write changes",
298                "Do nothing"
299             },
300             new String[] {
301                UserPrefs.AUTO_SAVE_ON_IDLE_BACKUP,
302                UserPrefs.AUTO_SAVE_ON_IDLE_COMMIT,
303                UserPrefs.AUTO_SAVE_ON_IDLE_NONE
304             },
305             false,
306             true,
307             false);
308       savePanel.add(autoSaveOnIdle_);
309       autoSaveIdleMs_ = new SelectWidget(
310             "Idle period: ",
311             new String[] {
312                "500ms",
313                "1000ms",
314                "1500ms",
315                "2000ms",
316                "3000ms",
317                "4000ms",
318                "5000ms",
319                "10000ms",
320             },
321             new String[] {
322                 "500",
323                 "1000",
324                 "1500",
325                 "2000",
326                 "3000",
327                 "4000",
328                 "5000",
329                 "10000"
330             },
331             false,
332             true,
333             false);
334       savePanel.add(autoSaveIdleMs_);
335 
336       VerticalTabPanel completionPanel = new VerticalTabPanel(ElementIds.EDIT_COMPLETION_PREFS);
337 
338       completionPanel.add(headerLabel("R and C/C++"));
339 
340       showCompletions_ = new SelectWidget(
341             "Show code completions:",
342             new String[] {
343                   "Automatically",
344                   "When Triggered ($, ::)",
345                   "Manually (Tab)"
346             },
347             new String[] {
348                   UserPrefs.CODE_COMPLETION_ALWAYS,
349                   UserPrefs.CODE_COMPLETION_TRIGGERED,
350                   UserPrefs.CODE_COMPLETION_MANUAL
351             },
352             false,
353             true,
354             false);
355 
356       spaced(showCompletions_);
357       completionPanel.add(showCompletions_);
358 
359       final CheckBox alwaysCompleteInConsole = checkboxPref(
360             "Allow automatic completions in console",
361             prefs.consoleCodeCompletion());
362       completionPanel.add(alwaysCompleteInConsole);
363 
364       showCompletions_.addChangeHandler(new ChangeHandler()
365       {
366          @Override
367          public void onChange(ChangeEvent event)
368          {
369             alwaysCompleteInConsole.setVisible(
370                    showCompletions_.getValue() == UserPrefs.CODE_COMPLETION_ALWAYS);
371 
372          }
373       });
374 
375       final CheckBox insertParensAfterFunctionCompletionsCheckbox =
376            checkboxPref("Insert parentheses after function completions",
377                  prefs.insertParensAfterFunctionCompletion());
378 
379       final CheckBox showSignatureTooltipsCheckbox =
380            checkboxPref("Show help tooltip after function completions",
381                  prefs.showFunctionSignatureTooltips());
382 
383       addEnabledDependency(
384             insertParensAfterFunctionCompletionsCheckbox,
385             showSignatureTooltipsCheckbox);
386 
387       completionPanel.add(insertParensAfterFunctionCompletionsCheckbox);
388       completionPanel.add(showSignatureTooltipsCheckbox);
389 
390       completionPanel.add(checkboxPref("Show help tooltip on cursor idle", prefs.showHelpTooltipOnIdle()));
391       completionPanel.add(checkboxPref("Insert spaces around equals for argument completions", prefs.insertSpacesAroundEquals()));
392       completionPanel.add(checkboxPref("Use tab for autocompletions", prefs.tabCompletion()));
393       completionPanel.add(checkboxPref("Use tab for multiline autocompletions", prefs.tabMultilineCompletion()));
394 
395 
396       Label otherLabel = headerLabel("Other Languages");
397       otherLabel.getElement().getStyle().setMarginTop(8, Unit.PX);
398       completionPanel.add(otherLabel);
399 
400       showCompletionsOther_ = new SelectWidget(
401             "Show code completions:",
402             new String[] {
403                   "Automatically",
404                   "Manually (Ctrl+Space) "
405             },
406             new String[] {
407                   UserPrefs.CODE_COMPLETION_OTHER_ALWAYS,
408                   UserPrefs.CODE_COMPLETION_OTHER_MANUAL
409             },
410             false,
411             true,
412             false);
413       completionPanel.add(showCompletionsOther_);
414 
415       Label otherTip = new Label(
416         "Keyword and text-based completions are supported for several other " +
417         "languages including JavaScript, HTML, CSS, Python, and SQL.");
418       otherTip.addStyleName(baseRes.styles().infoLabel());
419       completionPanel.add(nudgeRightPlus(otherTip));
420 
421 
422       Label delayLabel = headerLabel("Completion Delay");
423       delayLabel.getElement().getStyle().setMarginTop(14, Unit.PX);
424       completionPanel.add(delayLabel);
425 
426       completionPanel.add(nudgeRightPlus(alwaysCompleteChars_ =
427           numericPref("Show completions after characters entered:", 1, 99,
428                       prefs.codeCompletionCharacters())));
429       completionPanel.add(nudgeRightPlus(alwaysCompleteDelayMs_ =
430           numericPref("Show completions after keyboard idle (ms):", 0, 9999,
431                       prefs.codeCompletionDelay())));
432 
433 
434       VerticalTabPanel diagnosticsPanel = new VerticalTabPanel(ElementIds.EDIT_DIAGNOSTICS_PREFS);
435       diagnosticsPanel.add(headerLabel("R Diagnostics"));
436       final CheckBox chkShowRDiagnostics = checkboxPref("Show diagnostics for R", prefs.showDiagnosticsR());
437       diagnosticsPanel.add(chkShowRDiagnostics);
438 
439       final VerticalPanel rOptionsPanel = new VerticalPanel();
440       rOptionsPanel.add(checkboxPref("Enable diagnostics within R function calls", prefs.diagnosticsInRFunctionCalls()));
441       rOptionsPanel.add(checkboxPref("Check arguments to R function calls", prefs.checkArgumentsToRFunctionCalls()));
442       rOptionsPanel.add(checkboxPref("Check usage of '<-' in function call", prefs.checkUnexpectedAssignmentInFunctionCall()));
443       rOptionsPanel.add(checkboxPref("Warn if variable used has no definition in scope", prefs.warnIfNoSuchVariableInScope()));
444       rOptionsPanel.add(checkboxPref("Warn if variable is defined but not used", prefs.warnVariableDefinedButNotUsed()));
445       rOptionsPanel.add(checkboxPref("Provide R style diagnostics (e.g. whitespace)", prefs.styleDiagnostics()));
446       rOptionsPanel.setVisible(prefs.showDiagnosticsR().getValue());
447       chkShowRDiagnostics.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
448          @Override
449          public void onValueChange(ValueChangeEvent<Boolean> event)
450          {
451             rOptionsPanel.setVisible(event.getValue());
452          }
453       });
454 
455       diagnosticsPanel.add(rOptionsPanel);
456       diagnosticsPanel.add(checkboxPref("Prompt to install missing R packages discovered in R source files", prefs.autoDiscoverPackageDependencies()));
457 
458       Label diagOtherLabel = headerLabel("Other Languages");
459       diagnosticsPanel.add(spacedBefore(diagOtherLabel));
460       diagnosticsPanel.add(checkboxPref("Show diagnostics for C/C++", prefs.showDiagnosticsCpp()));
461       diagnosticsPanel.add(checkboxPref("Show diagnostics for JavaScript, HTML, and CSS", prefs.showDiagnosticsOther()));
462 
463       Label diagShowLabel = headerLabel("Show Diagnostics");
464       diagnosticsPanel.add(spacedBefore(diagShowLabel));
465       diagnosticsPanel.add(checkboxPref("Show diagnostics whenever source files are saved", prefs.diagnosticsOnSave()));
466       diagnosticsPanel.add(tight(checkboxPref("Show diagnostics after keyboard is idle for a period of time",
467             prefs.backgroundDiagnostics(), false /*defaultSpace*/)));
468       diagnosticsPanel.add(indent(backgroundDiagnosticsDelayMs_ =
469             numericPref("Keyboard idle time (ms):", 0, 9999, prefs.backgroundDiagnosticsDelayMs())));
470 
471       HelpLink diagnosticsHelpLink = new DiagnosticsHelpLink();
472       diagnosticsHelpLink.getElement().getStyle().setMarginTop(12, Unit.PX);
473       nudgeRight(diagnosticsHelpLink);
474       diagnosticsPanel.add(diagnosticsHelpLink);
475 
476 
477       DialogTabLayoutPanel tabPanel = new DialogTabLayoutPanel("Editing");
478       tabPanel.setSize("435px", "533px");
479       tabPanel.add(editingPanel, "Editing", editingPanel.getBasePanelId());
480       tabPanel.add(displayPanel, "Display", displayPanel.getBasePanelId());
481       tabPanel.add(savePanel, "Saving", savePanel.getBasePanelId());
482       tabPanel.add(completionPanel, "Completion", completionPanel.getBasePanelId());
483       tabPanel.add(diagnosticsPanel, "Diagnostics", diagnosticsPanel.getBasePanelId());
484       tabPanel.selectTab(0);
485       add(tabPanel);
486    }
487 
disable(CheckBox checkBox)488    private void disable(CheckBox checkBox)
489    {
490       checkBox.setValue(false);
491       checkBox.setEnabled(false);
492       checkBox.setVisible(false);
493    }
494 
enable(CheckBox checkBox)495    private void enable(CheckBox checkBox)
496    {
497       checkBox.setValue(true);
498       checkBox.setEnabled(true);
499       checkBox.setVisible(true);
500    }
501 
addEnabledDependency(final CheckBox speaker, final CheckBox listener)502    private void addEnabledDependency(final CheckBox speaker,
503                                      final CheckBox listener)
504    {
505       if (speaker.getValue() == false)
506          disable(listener);
507 
508       speaker.addValueChangeHandler(
509             new ValueChangeHandler<Boolean>()
510             {
511                @Override
512                public void onValueChange(ValueChangeEvent<Boolean> event)
513                {
514                   if (event.getValue() == false)
515                      disable(listener);
516                   else
517                      enable(listener);
518                }
519             });
520    }
521 
522    @Override
initialize(UserPrefs prefs)523    protected void initialize(UserPrefs prefs)
524    {
525       lineEndings_.setValue(prefs.lineEndingConversion().getValue());
526 
527       showCompletions_.setValue(prefs_.codeCompletion().getValue());
528       showCompletionsOther_.setValue(prefs_.codeCompletionOther().getValue());
529       editorMode_.setValue(prefs_.editorKeybindings().getValue());
530       foldMode_.setValue(prefs_.foldStyle().getValue());
531       delimiterSurroundWidget_.setValue(prefs_.surroundSelection().getValue());
532       executionBehavior_.setValue(prefs_.executionBehavior().getValue());
533       autoSaveOnIdle_.setValue(prefs_.autoSaveOnIdle().getValue());
534 
535       // To prevent users from choosing nonsensical or pathological values for
536       // the sensitive autosave idle option, act like they selected 1000ms (the
537       // default) if they've managed to load something invalid.
538       if (!autoSaveIdleMs_.setValue(prefs_.autoSaveIdleMs().getValue().toString()))
539       {
540          autoSaveIdleMs_.setValue("1000");
541       }
542    }
543 
544    @Override
onApply(UserPrefs prefs)545    public RestartRequirement onApply(UserPrefs prefs)
546    {
547       RestartRequirement restartRequirement = super.onApply(prefs);
548 
549       // editing prefs
550       prefs_.lineEndingConversion().setGlobalValue(lineEndings_.getValue());
551 
552       prefs_.defaultEncoding().setGlobalValue(encodingValue_);
553 
554       prefs_.codeCompletion().setGlobalValue(showCompletions_.getValue());
555       prefs_.codeCompletionOther().setGlobalValue(showCompletionsOther_.getValue());
556 
557       String editorMode = editorMode_.getValue();
558 
559       prefs_.editorKeybindings().setGlobalValue(editorMode);
560       boolean isVim = editorMode == UserPrefs.EDITOR_KEYBINDINGS_VIM;
561       boolean isEmacs = editorMode == UserPrefs.EDITOR_KEYBINDINGS_EMACS;
562       boolean isSublime = editorMode == UserPrefs.EDITOR_KEYBINDINGS_SUBLIME;
563 
564       if (isVim)
565          ShortcutManager.INSTANCE.setEditorMode(KeyboardShortcut.MODE_VIM);
566       else if (isEmacs)
567          ShortcutManager.INSTANCE.setEditorMode(KeyboardShortcut.MODE_EMACS);
568       else if (isSublime)
569          ShortcutManager.INSTANCE.setEditorMode(KeyboardShortcut.MODE_SUBLIME);
570       else
571          ShortcutManager.INSTANCE.setEditorMode(KeyboardShortcut.MODE_DEFAULT);
572 
573       prefs_.foldStyle().setGlobalValue(foldMode_.getValue());
574       prefs_.surroundSelection().setGlobalValue(delimiterSurroundWidget_.getValue());
575       prefs_.executionBehavior().setGlobalValue(executionBehavior_.getValue());
576       prefs_.autoSaveOnIdle().setGlobalValue(autoSaveOnIdle_.getValue());
577       prefs_.autoSaveIdleMs().setGlobalValue(StringUtil.parseInt(autoSaveIdleMs_.getValue(), 1000));
578 
579       return restartRequirement;
580    }
581 
582    @Override
getIcon()583    public ImageResource getIcon()
584    {
585       return new ImageResource2x(PreferencesDialogBaseResources.INSTANCE.iconCodeEditing2x());
586    }
587 
588    @Override
validate()589    public boolean validate()
590    {
591       return (!spacesForTab_.getValue() || tabWidth_.validate()) &&
592              (!showMargin_.getValue() || marginCol_.validate()) &&
593              alwaysCompleteChars_.validate() &&
594              alwaysCompleteDelayMs_.validate() &&
595              backgroundDiagnosticsDelayMs_.validate();
596    }
597 
598    @Override
getName()599    public String getName()
600    {
601       return "Code";
602    }
603 
setEncoding(String encoding)604    private void setEncoding(String encoding)
605    {
606       encodingValue_ = encoding;
607       if (StringUtil.isNullOrEmpty(encoding))
608          encoding_.setText(ChooseEncodingDialog.ASK_LABEL);
609       else
610          encoding_.setText(encoding);
611    }
612 
613    private final UserPrefs prefs_;
614    private final SourceServerOperations server_;
615    private final NumericValueWidget tabWidth_;
616    private final NumericValueWidget marginCol_;
617    private final LineEndingsSelectWidget lineEndings_;
618    private final NumericValueWidget alwaysCompleteChars_;
619    private final NumericValueWidget alwaysCompleteDelayMs_;
620    private final NumericValueWidget backgroundDiagnosticsDelayMs_;
621    private final CheckBox spacesForTab_;
622    private final CheckBox showMargin_;
623    private final SelectWidget showCompletions_;
624    private final SelectWidget showCompletionsOther_;
625    private final SelectWidget editorMode_;
626    private final SelectWidget foldMode_;
627    private final SelectWidget delimiterSurroundWidget_;
628    private final SelectWidget executionBehavior_;
629    private final SelectWidget autoSaveOnIdle_;
630    private final SelectWidget autoSaveIdleMs_;
631    private final TextBoxWithButton encoding_;
632    private String encodingValue_;
633 }
634