├── .gitignore ├── SingleFileExecutionPlugin.jar ├── SingleFileExecutionPlugin.iml ├── LICENSE ├── src ├── SingleFileExecutionConfigurable.java ├── SingleFileExecutionConfig.java ├── ExeOverwriteConfirmDialog.form ├── ExeOverwriteConfirmDialog.java ├── SingleFileExecutionConfigurableGUI.form ├── SingleFileExecutionConfigurableGUI.java └── SingleFileExecutionAction.java ├── resources └── META-INF │ └── plugin.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | -------------------------------------------------------------------------------- /SingleFileExecutionPlugin.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corochann/SingleFileExecutionPlugin/HEAD/SingleFileExecutionPlugin.jar -------------------------------------------------------------------------------- /SingleFileExecutionPlugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 corochann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/SingleFileExecutionConfigurable.java: -------------------------------------------------------------------------------- 1 | import com.intellij.openapi.options.ConfigurationException; 2 | import com.intellij.openapi.options.SearchableConfigurable; 3 | import com.intellij.openapi.project.Project; 4 | import org.jetbrains.annotations.Nls; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | 10 | /** 11 | * This ProjectConfigurable class appears on Settings dialog, 12 | * to let user to configure this plugin's behavior. 13 | */ 14 | public class SingleFileExecutionConfigurable implements SearchableConfigurable { 15 | 16 | private SingleFileExecutionConfigurableGUI mGUI; 17 | private final SingleFileExecutionConfig mConfig; 18 | 19 | @SuppressWarnings("FieldCanBeLocal") 20 | private final Project mProject; 21 | 22 | public SingleFileExecutionConfigurable(@NotNull Project project) { 23 | mProject = project; 24 | mConfig = SingleFileExecutionConfig.getInstance(project); 25 | } 26 | 27 | @Nls 28 | @Override 29 | public String getDisplayName() { 30 | return "Single File Execution Plugin"; 31 | } 32 | 33 | @Nullable 34 | @Override 35 | public String getHelpTopic() { 36 | return "preference.SingleFileExecutionConfigurable"; 37 | } 38 | 39 | @NotNull 40 | @Override 41 | public String getId() { 42 | return "preference.SingleFileExecutionConfigurable"; 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public Runnable enableSearch(String s) { 48 | return null; 49 | } 50 | 51 | @Nullable 52 | @Override 53 | public JComponent createComponent() { 54 | mGUI = new SingleFileExecutionConfigurableGUI(); 55 | mGUI.createUI(mProject); 56 | return mGUI.getRootPanel(); 57 | } 58 | 59 | @Override 60 | public boolean isModified() { 61 | return mGUI.isModified(); 62 | } 63 | 64 | @Override 65 | public void apply() throws ConfigurationException { 66 | mGUI.apply(); 67 | } 68 | 69 | @Override 70 | public void reset() { 71 | mGUI.reset(); 72 | } 73 | 74 | @Override 75 | public void disposeUIResources() { 76 | mGUI = null; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/SingleFileExecutionConfig.java: -------------------------------------------------------------------------------- 1 | import com.intellij.openapi.components.PersistentStateComponent; 2 | import com.intellij.openapi.components.ServiceManager; 3 | import com.intellij.openapi.components.State; 4 | import com.intellij.openapi.components.Storage; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.util.xmlb.XmlSerializerUtil; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * PersistentStateComponent keeps project config values. 11 | * Similar notion of 'preference' in Android 12 | */ 13 | @State( 14 | name="SingleFileExecutionConfig", 15 | storages = { 16 | @Storage("SingleFileExecutionConfig.xml")} 17 | ) 18 | public class SingleFileExecutionConfig implements PersistentStateComponent { 19 | 20 | /* NOTE: member should be "public" to be saved in xml */ 21 | /* add_executable(): exe name */ 22 | static final String EXECUTABLE_NAME_FILENAME = "%FILENAME%"; 23 | public static final String DEFAULT_EXECUTABLE_NAME = EXECUTABLE_NAME_FILENAME; 24 | public String executableName = DEFAULT_EXECUTABLE_NAME; // persistent member should be public 25 | /* set_target_properties(): runtime output directory */ 26 | static final String PROJECTDIR = "%PROJECTDIR%"; 27 | static final String FILEDIR = "%FILEDIR%"; 28 | public static final String DEFAULT_RUNTIME_OUTPUT_DIRECTORY = ""; 29 | public String runtimeOutputDirectory = ""; // set empty string as default, persistent member should be public 30 | private static final boolean DEFAULT_NOT_SHOW_OVERWRITE_CONFIRM_DIALOG = false; 31 | public boolean notShowOverwriteConfirmDialog = DEFAULT_NOT_SHOW_OVERWRITE_CONFIRM_DIALOG; // persistent member should be public 32 | 33 | SingleFileExecutionConfig() { } 34 | 35 | String getExecutableName() { 36 | if (executableName == null) { 37 | // Error, it should not happen 38 | executableName = ""; 39 | } 40 | return executableName; 41 | } 42 | 43 | void setExecutableName(String executableName) { 44 | this.executableName = executableName; 45 | } 46 | 47 | public String getRuntimeOutputDirectory() { 48 | return runtimeOutputDirectory; 49 | } 50 | 51 | public void setRuntimeOutputDirectory(String runtimeOutputDirectory) { 52 | this.runtimeOutputDirectory = runtimeOutputDirectory; 53 | } 54 | 55 | @Nullable 56 | @Override 57 | public SingleFileExecutionConfig getState() { 58 | return this; 59 | } 60 | 61 | @Override 62 | public void loadState(SingleFileExecutionConfig singleFileExecutionConfig) { 63 | XmlSerializerUtil.copyBean(singleFileExecutionConfig, this); 64 | } 65 | 66 | @Nullable 67 | public static SingleFileExecutionConfig getInstance(Project project) { 68 | SingleFileExecutionConfig sfec = ServiceManager.getService(project, SingleFileExecutionConfig.class); 69 | return sfec; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.corochann.plugin.clion.singlefileexecutionplugin 3 | C/C++ Single File Execution 4 | 1.3 5 | corochann 6 | 7 | CMakeLists.txt needs to be configured 9 | to declare add_executable(). 10 | It is troublesome when you want to run many of the files independently within the same project.
11 | This plugin supports to insert add_executable() statement into CMakeLists.txt, so that you can easily 12 | build and run a single file. 13 | Also, you can specify a location of directory for build executable file for each target.
14 | Links:
15 | 19 | ]]>
20 | 21 | version 1.3: Bug fix for config. - Settings was not saved persistently

23 |

version 1.2:

24 |
    25 |
  • Add set_target_properties() configuration for target specific runtime output directory setting.
  • 26 |
  • Bug fix: handle directory name including whitespace
  • 27 |
28 |

version 1.1: Bug fix.

29 | ]]> 30 |
31 | 32 | 33 | 34 | 35 | 37 | 41 | com.intellij.modules.lang 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SingleFileExecutionPlugin 2 | [CLion](https://www.jetbrains.com/clion/) plugin to execute single file .c/.cpp file quickly. 3 | 4 | ## Description 5 | CLion is a C/C++ IDE on [IntelliJ IDEA](https://www.jetbrains.com/idea/) platform provided by [JetBrains](https://www.jetbrains.com/). 6 | 7 | CLion is working on CMake platform, so when you want to run a single file with main() function you need to configure CMakeLists.txt file everytime. 8 | This plugin helps you to add a configuration to quickly run a single .c/.cpp file. 9 | 10 | Links 11 | 12 | - **[Document page](http://corochann.com/projects/single-file-execution-plugin)**: for more detail information. 13 | - **[Official plugin page](https://plugins.jetbrains.com/plugin/8352?pr=)** 14 | 15 |

Installation

16 |

C/C++ Single File Execution plugin is uploaded on JetBrains repositry, so you can download by navigating [File] → [Settings] → [Plugins] tab → click [Browse repositries...] in CLion.

17 |

You can find and install C/C++ Single File Execution plugin.

18 |

install_form_repositry

19 |

 

20 |

How to use

21 |
    22 |
  1. Create or show C/C++ source code you want to run on the editor.
  2. 23 |
  3. Right click on the editor.
  4. 24 |
  5. Select "Add executable for single c/cpp file".
  6. 25 |
26 |

That's all! Plugin automatically insert add_executable to CMakeLists.txt with proper path for you.

27 |

sample_example2

28 |

After that, you can start coding and once it's done, run it by selecting proper executable name on the top-right panel in CLion.

29 |

sample_example_run_bar

30 |

Configuration

31 |

single_file_execution_settings_v120

32 |

Little configuration is available from [File]  → [Settings] → [Tools] tab > [Single File Execution Plugin].

33 |

executable name

34 |

You may specify the executable name. As a default with %FILENAME%, it will use a name depending on the source code file name. In this case, every time you add a new source code and insert add_executable, new executable name will be added. Build configuration tab can be messy in this case as the number of files increases.

35 |

Another way is to use "fixed" executable name here (not use %FILENAME%), in that case you can always run single source code file with same executable name.

36 |

runtime output directory

37 |

You can also specify a directory where the executable file will be stored after build.

38 |

For example, when you set %FILEDIR%, executable file will be located in the same directory of source file. This configuration is especially useful when your source code reads/writes another file which is located in same directory of the source file.

39 |

Concrete example is for programming contest. You may want to read the input data from another text file. You can just place the input data text file in the same directory of source file, and just write below

40 |
int main() {
41 |     freopen("input_data.txt", "r", stdin);
42 | 
43 |     ...
44 | 
45 | }
46 |

you can now build and run this program by pressing "run" button on CLion.

47 | -------------------------------------------------------------------------------- /src/ExeOverwriteConfirmDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
81 | -------------------------------------------------------------------------------- /src/ExeOverwriteConfirmDialog.java: -------------------------------------------------------------------------------- 1 | import com.intellij.openapi.project.Project; 2 | import com.intellij.uiDesigner.core.GridConstraints; 3 | import com.intellij.uiDesigner.core.GridLayoutManager; 4 | import com.intellij.uiDesigner.core.Spacer; 5 | 6 | import javax.swing.*; 7 | import javax.swing.event.ChangeEvent; 8 | import javax.swing.event.ChangeListener; 9 | import java.awt.*; 10 | import java.awt.event.*; 11 | 12 | public class ExeOverwriteConfirmDialog extends JDialog { 13 | 14 | private JPanel contentPane; 15 | private JButton buttonOK; 16 | private JButton buttonCancel; 17 | private JCheckBox doNotShowItCheckBox; 18 | 19 | public static final int OK_FLAG_CANCEL = 0; 20 | public static final int OK_FLAG_OK = 1; 21 | /** 22 | * 0: not OK (when cancel button pressed etc.) 23 | * 1: OK (when ok button pressed & doNotShowItCheckBox is not selected) 24 | */ 25 | private int okFlag = OK_FLAG_CANCEL; 26 | private static SingleFileExecutionConfig config; 27 | 28 | 29 | public ExeOverwriteConfirmDialog() { 30 | setContentPane(contentPane); 31 | setModal(true); 32 | getRootPane().setDefaultButton(buttonOK); 33 | 34 | buttonOK.addActionListener(new ActionListener() { 35 | public void actionPerformed(ActionEvent e) { 36 | onOK(); 37 | } 38 | }); 39 | 40 | buttonCancel.addActionListener(new ActionListener() { 41 | public void actionPerformed(ActionEvent e) { 42 | onCancel(); 43 | } 44 | }); 45 | 46 | // call onCancel() when cross is clicked 47 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 48 | addWindowListener(new WindowAdapter() { 49 | public void windowClosing(WindowEvent e) { 50 | onCancel(); 51 | } 52 | }); 53 | 54 | // call onCancel() on ESCAPE 55 | contentPane.registerKeyboardAction(new ActionListener() { 56 | public void actionPerformed(ActionEvent e) { 57 | onCancel(); 58 | } 59 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 60 | } 61 | 62 | private void onOK() { 63 | // add your code here 64 | okFlag = OK_FLAG_OK; 65 | dispose(); 66 | } 67 | 68 | private void onCancel() { 69 | // add your code here if necessary 70 | okFlag = OK_FLAG_CANCEL; 71 | dispose(); 72 | } 73 | 74 | /** return okFlag, indicates user pressed ok or not */ 75 | public static int show(Project project) { 76 | ExeOverwriteConfirmDialog dialog = new ExeOverwriteConfirmDialog(); 77 | config = SingleFileExecutionConfig.getInstance(project); 78 | dialog.setLocationRelativeTo(null); 79 | dialog.doNotShowItCheckBox.setSelected(config.notShowOverwriteConfirmDialog); 80 | dialog.doNotShowItCheckBox.addChangeListener(new ChangeListener() { 81 | @Override 82 | public void stateChanged(ChangeEvent e) { 83 | config.notShowOverwriteConfirmDialog = dialog.doNotShowItCheckBox.isSelected(); 84 | } 85 | }); 86 | dialog.pack(); 87 | dialog.setVisible(true); 88 | return dialog.okFlag; 89 | } 90 | 91 | // not used, instead use show 92 | public static void main(String[] args) { 93 | ExeOverwriteConfirmDialog dialog = new ExeOverwriteConfirmDialog(); 94 | dialog.pack(); 95 | dialog.setVisible(true); 96 | //System.exit(0); 97 | } 98 | 99 | { 100 | // GUI initializer generated by IntelliJ IDEA GUI Designer 101 | // >>> IMPORTANT!! <<< 102 | // DO NOT EDIT OR ADD ANY CODE HERE! 103 | $$$setupUI$$$(); 104 | } 105 | 106 | /** 107 | * Method generated by IntelliJ IDEA GUI Designer 108 | * >>> IMPORTANT!! <<< 109 | * DO NOT edit this method OR call it in your code! 110 | * 111 | * @noinspection ALL 112 | */ 113 | private void $$$setupUI$$$() { 114 | contentPane = new JPanel(); 115 | contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); 116 | final JPanel panel1 = new JPanel(); 117 | panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); 118 | contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); 119 | final Spacer spacer1 = new Spacer(); 120 | panel1.add(spacer1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 121 | final JPanel panel2 = new JPanel(); 122 | panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); 123 | panel1.add(panel2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 124 | buttonOK = new JButton(); 125 | buttonOK.setText("OK"); 126 | panel2.add(buttonOK, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 127 | buttonCancel = new JButton(); 128 | buttonCancel.setText("Cancel"); 129 | panel2.add(buttonCancel, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 130 | final JPanel panel3 = new JPanel(); 131 | panel3.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); 132 | contentPane.add(panel3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 133 | final JLabel label1 = new JLabel(); 134 | label1.setText("Executable already exists with different source file, do you want to overwrite?"); 135 | panel3.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 136 | doNotShowItCheckBox = new JCheckBox(); 137 | doNotShowItCheckBox.setText("Do not show it again."); 138 | panel3.add(doNotShowItCheckBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 139 | } 140 | 141 | /** 142 | * @noinspection ALL 143 | */ 144 | public JComponent $$$getRootComponent$$$() { 145 | return contentPane; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/SingleFileExecutionConfigurableGUI.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 | -------------------------------------------------------------------------------- /src/SingleFileExecutionConfigurableGUI.java: -------------------------------------------------------------------------------- 1 | import com.intellij.openapi.project.Project; 2 | import com.intellij.uiDesigner.core.GridConstraints; 3 | import com.intellij.uiDesigner.core.GridLayoutManager; 4 | import com.intellij.uiDesigner.core.Spacer; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | 9 | /** 10 | * GUI for the {@link SingleFileExecutionConfigurable} 11 | */ 12 | public class SingleFileExecutionConfigurableGUI { 13 | private JPanel rootPanel; 14 | private JTextField exeNameTextField; 15 | private JCheckBox notShowDialogCheckBox; 16 | private JPanel showConfimationDialogSetting; 17 | private JPanel outputPathSetting; 18 | private JPanel exeNameSetting; 19 | private JTextField runtimeOutputDirectoryTextField; 20 | private SingleFileExecutionConfig mConfig; 21 | 22 | SingleFileExecutionConfigurableGUI() { 23 | // $$$setupUI$$$ will be executed here (inserted automatically) 24 | } 25 | 26 | public void createUI(Project project) { 27 | mConfig = SingleFileExecutionConfig.getInstance(project); 28 | exeNameTextField.setText(mConfig.getExecutableName()); 29 | runtimeOutputDirectoryTextField.setText(mConfig.getRuntimeOutputDirectory()); 30 | notShowDialogCheckBox.setSelected(mConfig.notShowOverwriteConfirmDialog); 31 | } 32 | 33 | public JPanel getRootPanel() { 34 | return rootPanel; 35 | } 36 | 37 | public JTextField getExeNameTextField() { 38 | return exeNameTextField; 39 | } 40 | 41 | public boolean isModified() { 42 | boolean modified = false; 43 | modified |= !exeNameTextField.getText().equals(mConfig.getExecutableName()); 44 | modified |= !runtimeOutputDirectoryTextField.getText().equals(mConfig.getRuntimeOutputDirectory()); 45 | modified |= !notShowDialogCheckBox.isSelected() == mConfig.notShowOverwriteConfirmDialog; 46 | return modified; 47 | } 48 | 49 | public void apply() { 50 | mConfig.setExecutableName(exeNameTextField.getText()); 51 | mConfig.setRuntimeOutputDirectory(runtimeOutputDirectoryTextField.getText()); 52 | mConfig.notShowOverwriteConfirmDialog = notShowDialogCheckBox.isSelected(); 53 | } 54 | 55 | public void reset() { 56 | exeNameTextField.setText(mConfig.getExecutableName()); 57 | runtimeOutputDirectoryTextField.setText(mConfig.getRuntimeOutputDirectory()); 58 | notShowDialogCheckBox.setSelected(mConfig.notShowOverwriteConfirmDialog); 59 | } 60 | 61 | { 62 | // GUI initializer generated by IntelliJ IDEA GUI Designer 63 | // >>> IMPORTANT!! <<< 64 | // DO NOT EDIT OR ADD ANY CODE HERE! 65 | $$$setupUI$$$(); 66 | } 67 | 68 | /** 69 | * Method generated by IntelliJ IDEA GUI Designer 70 | * >>> IMPORTANT!! <<< 71 | * DO NOT edit this method OR call it in your code! 72 | * 73 | * @noinspection ALL 74 | */ 75 | private void $$$setupUI$$$() { 76 | rootPanel = new JPanel(); 77 | rootPanel.setLayout(new GridLayoutManager(4, 2, new Insets(0, 0, 0, 0), -1, -1)); 78 | rootPanel.setRequestFocusEnabled(true); 79 | final Spacer spacer1 = new Spacer(); 80 | rootPanel.add(spacer1, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 81 | showConfimationDialogSetting = new JPanel(); 82 | showConfimationDialogSetting.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); 83 | rootPanel.add(showConfimationDialogSetting, new GridConstraints(2, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 84 | notShowDialogCheckBox = new JCheckBox(); 85 | notShowDialogCheckBox.setText("Do not show confirmation dialog when same executable name already exists."); 86 | showConfimationDialogSetting.add(notShowDialogCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 87 | final Spacer spacer2 = new Spacer(); 88 | showConfimationDialogSetting.add(spacer2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 89 | outputPathSetting = new JPanel(); 90 | outputPathSetting.setLayout(new GridLayoutManager(4, 2, new Insets(0, 0, 0, 0), -1, -1)); 91 | rootPanel.add(outputPathSetting, new GridConstraints(1, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 92 | final JLabel label1 = new JLabel(); 93 | label1.setText("Runtime output directory"); 94 | outputPathSetting.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 95 | runtimeOutputDirectoryTextField = new JTextField(); 96 | outputPathSetting.add(runtimeOutputDirectoryTextField, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 97 | final JLabel label2 = new JLabel(); 98 | label2.setText("%PROJECTDIR% will be replaced with project root directory."); 99 | outputPathSetting.add(label2, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 100 | final JLabel label3 = new JLabel(); 101 | label3.setText("%FILEDIR% will be replaced with source file's parent directory."); 102 | outputPathSetting.add(label3, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 103 | final JLabel label4 = new JLabel(); 104 | label4.setText("Default path will be used when not specified."); 105 | outputPathSetting.add(label4, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 106 | exeNameSetting = new JPanel(); 107 | exeNameSetting.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1)); 108 | rootPanel.add(exeNameSetting, new GridConstraints(0, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 109 | final JLabel label5 = new JLabel(); 110 | label5.setText("Executable name"); 111 | exeNameSetting.add(label5, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(80, 16), null, 0, false)); 112 | exeNameTextField = new JTextField(); 113 | exeNameTextField.setAutoscrolls(true); 114 | exeNameTextField.setEditable(true); 115 | exeNameTextField.setEnabled(true); 116 | exeNameTextField.setHorizontalAlignment(10); 117 | exeNameSetting.add(exeNameTextField, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 118 | final JLabel label6 = new JLabel(); 119 | label6.setText("%FILENAME% will be replaced to actual filename without extension."); 120 | label6.setVerticalAlignment(0); 121 | exeNameSetting.add(label6, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 122 | label5.setLabelFor(exeNameTextField); 123 | } 124 | 125 | /** 126 | * @noinspection ALL 127 | */ 128 | public JComponent $$$getRootComponent$$$() { 129 | return rootPanel; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/SingleFileExecutionAction.java: -------------------------------------------------------------------------------- 1 | import com.intellij.notification.Notification; 2 | import com.intellij.notification.NotificationType; 3 | import com.intellij.notification.Notifications; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.CommonDataKeys; 7 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 8 | import com.intellij.openapi.application.ApplicationManager; 9 | import com.intellij.openapi.diagnostic.Logger; 10 | import com.intellij.openapi.editor.Document; 11 | import com.intellij.openapi.editor.Editor; 12 | import com.intellij.openapi.fileEditor.FileDocumentManager; 13 | import com.intellij.openapi.project.Project; 14 | import com.intellij.openapi.vfs.LocalFileSystem; 15 | import com.intellij.openapi.vfs.VirtualFile; 16 | 17 | import java.io.File; 18 | import java.util.Scanner; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | /** 23 | * 24 | */ 25 | public class SingleFileExecutionAction extends AnAction { 26 | private static final Logger LOG = Logger.getInstance(SingleFileExecutionAction.class.getSimpleName()); 27 | 28 | public static final int EXE_NOT_EXIST = 0; 29 | public static final int EXE_EXIST_SAME_SOURCE = 1; 30 | public static final int EXE_EXIST_DIFFERENT_SOURCE = 2; 31 | private VirtualFile sourceFile; 32 | private SingleFileExecutionConfig config; 33 | private Project project; 34 | 35 | @Override 36 | public void actionPerformed(AnActionEvent e) { 37 | 38 | /* Get all the required data from data keys */ 39 | //final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR); 40 | project = e.getRequiredData(CommonDataKeys.PROJECT); 41 | config = SingleFileExecutionConfig.getInstance(project); 42 | String cmakelistFilePath = project.getBasePath() + "/CMakeLists.txt"; 43 | 44 | //Access document, caret, and selection 45 | //final Document document = editor.getDocument(); 46 | //final SelectionModel selectionModel = editor.getSelectionModel(); 47 | //final int start = selectionModel.getSelectionStart(); 48 | //final int end = selectionModel.getSelectionEnd(); 49 | 50 | File file = new File(cmakelistFilePath); 51 | VirtualFile cmakelistFile = LocalFileSystem.getInstance().findFileByIoFile(file); 52 | if (cmakelistFile == null) { 53 | /* CMakeLists.txt not exist */ 54 | Notifications.Bus.notify ( 55 | new Notification("singlefileexecutionaction", "Single File Execution Plugin", "Fail to access " + cmakelistFilePath, NotificationType.ERROR) 56 | ); 57 | return; 58 | } 59 | Document cmakelistDocument = FileDocumentManager.getInstance().getDocument(cmakelistFile); 60 | 61 | // get source file (* currently selected file in editor) 62 | sourceFile = e.getData(PlatformDataKeys.VIRTUAL_FILE); 63 | //vFile.getCanonicalPath(); // source file path (absolute path) 64 | //vFile.getPath(); // source file path (absolute path) 65 | String fileName = sourceFile != null ? sourceFile.getName() : null; // source file name (but not include path) 66 | 67 | String exeName = buildExeName(config.getExecutableName()); 68 | String sourceName = fileName; 69 | String relativeSourcePath = new File(project.getBasePath()).toURI().relativize(new File(sourceFile.getPath()).toURI()).getPath(); 70 | 71 | /* parse cmakelistDocument to check existence of exe_name */ 72 | /* See http://mmasashi.hatenablog.com/entry/20091129/1259511129 for lazy, greedy search */ 73 | String regex = "^add_executable\\s*?\\(\\s*?" + exeName + "\\s+(((\\S+)\\s+)*\\S+)\\s*\\)"; 74 | 75 | Pattern pattern = Pattern.compile(regex); 76 | 77 | Scanner scanner = new Scanner(cmakelistDocument.getText()); 78 | int exeExistFlag = EXE_NOT_EXIST; 79 | while (scanner.hasNextLine()) { 80 | String line = scanner.nextLine(); 81 | Matcher m = pattern.matcher(line); 82 | if (m.find()) { 83 | //String existingExeName = m.group(1); 84 | String existingSourceName = m.group(1); 85 | if (existingSourceName.contains(relativeSourcePath)) { 86 | exeExistFlag = EXE_EXIST_SAME_SOURCE; 87 | } else { 88 | exeExistFlag = EXE_EXIST_DIFFERENT_SOURCE; 89 | } 90 | break; 91 | } 92 | } 93 | scanner.close(); 94 | 95 | //LocalFileSystem.getInstance().findFileByIoFile(); 96 | switch(exeExistFlag) { 97 | case EXE_NOT_EXIST: 98 | insertAddExecutable(cmakelistDocument, exeName, relativeSourcePath); 99 | Notifications.Bus.notify ( 100 | new Notification("singlefileexecutionaction", "Single File Execution Plugin", "add_executable added for " + sourceName + ".", NotificationType.INFORMATION) 101 | ); 102 | break; 103 | case EXE_EXIST_SAME_SOURCE: 104 | // skip setText 105 | Notifications.Bus.notify ( 106 | new Notification("singlefileexecutionaction", "Single File Execution Plugin", "add_executable for this source already exists.", NotificationType.INFORMATION) 107 | ); 108 | break; 109 | case EXE_EXIST_DIFFERENT_SOURCE: 110 | int okFlag; 111 | if (config.notShowOverwriteConfirmDialog) { 112 | // Do not show dialog & proceed 113 | okFlag = ExeOverwriteConfirmDialog.OK_FLAG_OK; 114 | } else { 115 | okFlag = ExeOverwriteConfirmDialog.show(project); 116 | } 117 | 118 | if (okFlag == ExeOverwriteConfirmDialog.OK_FLAG_OK) { 119 | // Ok 120 | updateAddExecutable(cmakelistDocument, exeName, relativeSourcePath); 121 | Notifications.Bus.notify( 122 | new Notification("singlefileexecutionaction", "Single File Execution Plugin", "add_executable overwritten", NotificationType.INFORMATION) 123 | ); 124 | } else { 125 | // cancel 126 | // do nothing so far 127 | } 128 | break; 129 | } 130 | 131 | } 132 | 133 | private void insertAddExecutable(final Document cmakelistDocument, final String exeName, final String relativeSourcePath) { 134 | ApplicationManager.getApplication().runWriteAction(new Runnable() { 135 | @Override 136 | public void run() { 137 | String updatedText = cmakelistDocument.getText(); 138 | /* add_executable statement */ 139 | updatedText += "\n" + constructAddExecutable(exeName, relativeSourcePath); 140 | /* set_target_properties statement */ 141 | String runtimeDir = config.getRuntimeOutputDirectory(); 142 | if (runtimeDir != null && !runtimeDir.equals("")) { 143 | String outputDir = quoteString(buildRuntimeOutputDirectory()); 144 | updatedText += "\n" + constructSetTargetProperties(exeName, outputDir); 145 | } 146 | cmakelistDocument.setText(updatedText); 147 | } 148 | }); 149 | } 150 | 151 | private void updateAddExecutable(final Document cmakelistDocument, final String exeName, final String relativeSourcePath) { 152 | String runtimeDir = config.getRuntimeOutputDirectory(); 153 | String updatedDocument = ""; 154 | 155 | /* 156 | * This regular expression finds 157 | * "add_executable(XXXX YYYY.cpp ZZZZ.cpp)" where XXXX is executable name, YYYY.cpp and ZZZZ.cpp are the source files. 158 | */ 159 | String regex = "^add_executable\\s*?\\(\\s*?" + exeName + "\\s+(((\\S+)\\s+)*\\S+)\\s*\\)"; 160 | Pattern pattern = Pattern.compile(regex); 161 | 162 | String regex2 = "^set_target_properties\\s*?\\(\\s*?" + exeName + "\\s+(((\\S+)\\s+)*\\S+)\\s*\\)"; 163 | Pattern pattern2 = Pattern.compile(regex2); 164 | 165 | Scanner scanner = new Scanner(cmakelistDocument.getText()); 166 | 167 | while (scanner.hasNextLine()) { 168 | String line = scanner.nextLine(); 169 | 170 | Matcher m = pattern.matcher(line); 171 | Matcher m2 = pattern2.matcher(line); 172 | if (m2.find()) { 173 | /* Skip adding line for old "set_target_properties()" statement */ 174 | continue; 175 | } 176 | if (m.find()) { 177 | /* add_executable */ 178 | line = m.replaceFirst(constructAddExecutable(exeName, relativeSourcePath)); 179 | /* set_target_properties */ 180 | if (runtimeDir != null && !runtimeDir.equals("")) { 181 | String outputDir = quoteString(buildRuntimeOutputDirectory()); 182 | line += "\n" + constructSetTargetProperties(exeName, outputDir); 183 | } 184 | } 185 | updatedDocument += line + '\n'; 186 | } 187 | scanner.close(); 188 | final String updatedText = updatedDocument; 189 | ApplicationManager.getApplication().runWriteAction(new Runnable() { 190 | @Override 191 | public void run() { 192 | cmakelistDocument.setText(updatedText); 193 | } 194 | }); 195 | } 196 | 197 | /** building add_executable(exeName sourceFilePath) statement */ 198 | private String constructAddExecutable(String exeName, String sourceFilePath) { 199 | return "add_executable("+ exeName + " " + quotingSourcePath(sourceFilePath) +")"; 200 | } 201 | 202 | /** building set_target_properties(exeName PROPERTIES RUNTIME_OUTPUT_DIRECTORY ourputDir) statement */ 203 | private String constructSetTargetProperties(String exeName, String outputDir) { 204 | return "set_target_properties(" + exeName + " PROPERTIES RUNTIME_OUTPUT_DIRECTORY " + outputDir + ")"; 205 | } 206 | 207 | /** build target exeName according based on the configuration */ 208 | private String buildExeName(String exeName) { 209 | String newExeName; 210 | /* %FILENAME% replacement */ 211 | newExeName = exeName.replace(SingleFileExecutionConfig.EXECUTABLE_NAME_FILENAME, sourceFile.getNameWithoutExtension()); 212 | return newExeName; 213 | } 214 | 215 | private String buildRuntimeOutputDirectory() { 216 | String newRuntimeOutputDirectory = config.getRuntimeOutputDirectory(); 217 | /* source file's parent directory absolute path */ 218 | //String sourceDir = new File(sourceFile.getPath()).getAbsoluteFile().getParentFile().getName(); 219 | String sourceDirRelativePath = new File(project.getBasePath()).toURI().relativize( 220 | new File(sourceFile.getPath()).getParentFile().toURI()).getPath(); 221 | 222 | newRuntimeOutputDirectory = newRuntimeOutputDirectory.replace(SingleFileExecutionConfig.PROJECTDIR, "${PROJECT_SOURCE_DIR}"); 223 | newRuntimeOutputDirectory = newRuntimeOutputDirectory.replace(SingleFileExecutionConfig.FILEDIR, "${CMAKE_CURRENT_SOURCE_DIR}/" + sourceDirRelativePath); 224 | return newRuntimeOutputDirectory; 225 | } 226 | 227 | private String quotingSourcePath(String path) { 228 | String quotedPath = path; 229 | if (path.contains(" ") || path.contains("(") || path.contains(")")) { 230 | quotedPath = '"' + quotedPath + '"'; 231 | } 232 | return quotedPath; 233 | } 234 | 235 | private String quoteString(String str) { 236 | return '"' + str + '"'; 237 | } 238 | 239 | @Override 240 | public void update(AnActionEvent e) { 241 | final Project project = e.getData(CommonDataKeys.PROJECT); 242 | final Editor editor = e.getData(CommonDataKeys.EDITOR); 243 | 244 | e.getPresentation().setVisible((project != null && editor != null)); 245 | } 246 | } 247 | --------------------------------------------------------------------------------