zoukankan      html  css  js  c++  java
  • editor

    1. Working with text

    The following set of steps will show how to access a text selection and change it.

    1.1. Pre-requirements

    1.1.1 Creating a new action

    In this example access to the Editor is made through an action as a plug-in point. To create an action we need deriveAnAction.java class.

    public class EditorIllustration extends AnAction {
    }
    

    1.1.2. Registering an action

    To register the action we should add a corresponding attribute to the <actions> section of the plugin configuration fileplugin.xml

    <actions>
        <action id="EditorBasics.EditorIllustration" class="EditorIllustration" text="Editor Basics"
            description="Illustrates how to plug an action in">
        <add-to-group group-id="EditorPopupMenu" anchor="last"/>
    </action>
    

    If an action is registered in the group EditorPopupMenu, like the sample above shows, it will be available from the context menu when the focus is located in the editor.

    1.1.3. Defining action’s visibility

    To determine conditions by which the action will be visible and available for being executed we need to override it’spublic void update(final AnActionEvent e) method.

    public class EditorIllustration extends AnAction {
        @Override
        public void update(final AnActionEvent e) {
        }
    }
    

    If we want to work with a selected part of the text, it’s reasonable to make the action available only when the following requirements are met:

    • There is a project open
    • There is an instance of the Editor available
    • There is a text selection in the Editor

    Further steps will show how to check these conditions through obtaining instances of Project and Editor and how to set up a desired level of action’s visibility.

    1.2. Getting an instance of the Active Editor

    A reference to an instance of the Editor can be obtained by calling CommonDataKeys.EDITOR, obtaining a project reference is performed the same way CommonDataKeys.PROJECT.

    public class EditorIllustration extends AnAction {
        @Override
        public void update(final AnActionEvent e) {
            //Get required data keys
            final Project project = e.getData(CommonDataKeys.PROJECT);
            final Editor editor = e.getData(CommonDataKeys.EDITOR);
            //Set visibility only in case of existing project and editor
            e.getPresentation().setVisible((project != null && editor != null));
        }
    }
    

    Note:

    To access an Editor instance also can be used other ways:

    • If DataContext object is available final Editor editor = CommonDataKeys.EDITOR.getData(context);

    • If ActionEvent object is available final Editor editor = actionEvent.getData(CommonDataKeys.EDITOR);

    1.3. Obtaining a caret model and selection

    After making sure we have a project open and an instance of the Editor we need to check if any selection is available and set action’s visibility accordingly to these conditions. SelectionModel got from the Editor allows to do it by calling it’shasSelection() method. Here’s how our update(final AnActionEvent e) method should look like at the end:

    public class EditorIllustration extends AnAction {
        @Override
        public void update(final AnActionEvent e) {
            //Get required data keys
            final Project project = e.getData(CommonDataKeys.PROJECT);
            final Editor editor = e.getData(CommonDataKeys.EDITOR);
            //Set visibility only in case of existing project and editor and if some text in the editor is selected
            e.getPresentation().setVisible((project != null && editor != null
            && editor.getSelectionModel().hasSelection()));
        }
    }
    

    Note: Editor allows to access different models of text representation. Model classes are located in editor subpackage of the editor-ui-api package and include:

    1.4. Obtaining a Document

    The action is visible and available now. In order to make it do something we need to override it’s public void actionPerformed(final AnActionEvent anActionEvent) method.

    public class EditorIllustration extends AnAction {
        @Override
        public void update(final AnActionEvent e) {
        //code here
        }
        @Override
        public void actionPerformed(final AnActionEvent anActionEvent) {
        }
    }
    

    To modify the text an instance of the Document needs to be accessed. Document represents the contents of a text file loaded into memory, and possibly opened in an IDEA text editor. The instance of a Document will be use later when a text replacement is performed. We also need to figure out where the selected part of the text is located.

    @Override
    public void actionPerformed(final AnActionEvent anActionEvent) {
        //Get all the required data from data keys
        final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
        final Project project = anActionEvent.getRequiredData(CommonDataKeys.PROJECT);
        //Access document, caret, and selection
        final Document document = editor.getDocument();
        final SelectionModel selectionModel = editor.getSelectionModel();
        final int start = selectionModel.getSelectionStart();
        final int end = selectionModel.getSelectionEnd();
    }
    

    1.5. Modifying text

    Generally replacement can be done by calling void replaceString(int startOffset, int endOffset, @NotNull CharSequence s); of the Document, however, the operation of replacement must be executed safely, this means the Document must be locked and any changes should be performed under the write action. See the Threading Issuessection to learn more about synchronization issues and changes safety on the IntelliJ Platform.

    @Override
    public void actionPerformed(final AnActionEvent anActionEvent) {
        //Get all the required data from data keys
        final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
        final Project project = anActionEvent.getRequiredData(CommonDataKeys.PROJECT);
        //Access document, caret, and selection
        final Document document = editor.getDocument();
        final SelectionModel selectionModel = editor.getSelectionModel();
    
        final int start = selectionModel.getSelectionStart();
        final int end = selectionModel.getSelectionEnd();
        //New instance of Runnable to make a replacement
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                document.replaceString(start, end, "Replacement");
            }
        };
        //Making the replacement
        WriteCommandAction.runWriteCommandAction(project, runnable);
        selectionModel.removeSelection();
    }
    

    String replacement action


    The source code is located in EditorIllustration.java. To see how text replacement works, check out Editor Basics plugin, make the project, and run it, then invoke the EditorIllustration action which is available in the context menu of the editor.

    2. Editor coordinates system. Positions and offsets

    Every caret in the editor has a set of properties describing it’s coordinates. These properties can be accessed by obtaining a caret model instance. Working with caret positions and it’s logical and visual properties will be explained in the sample below.

    2.1. Pre-requirements

    Access to the Editor is performed through an action.

    2.2. Accessing caret positions

    To get an access to caret positions an instance of CaretModel should be obtained.

    public class EditorAreaIllustration extends AnAction {
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            CaretModel caretModel = editor.getCaretModel();
        }
        @Override
        public void update(AnActionEvent e) {
           //...
        }
    }
    

    2.3. Logical position

    LogicalPosition.java represents a line and a column of the current logical position of the caret. Logical positions ignore folding - for example, if the top 10 lines of the document are folded, the 10th line in the document will have the line number 10 in its logical position.

    public class EditorAreaIllustration extends AnAction {
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            CaretModel caretModel = editor.getCaretModel();
            LogicalPosition logicalPosition = caretModel.getLogicalPosition();
        }
        @Override
        public void update(AnActionEvent e) {
            //...
        }
    }
    

    Logical position may store additional parameters that define its mapping to VisualPosition.java. Rationale is that single logical pair matches soft wrap-introduced virtual space, i.e. different visual positions correspond to the same logical position. It’s convenient to store exact visual location details within the logical position in order to relief further ‘logical position’ -> ‘visual position’ mapping.

    2.4. Visual position

    VisualPosition.java represent a visual position and may very from the corresponding logical position. Visual positions take folding into account - for example, if the top 10 lines of the document are folded, the 10th line in the document will have the line number 1 in its visual position.

    public class EditorAreaIllustration extends AnAction {
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            CaretModel caretModel = editor.getCaretModel();
            LogicalPosition logicalPosition = caretModel.getLogicalPosition();
            VisualPosition visualPosition = caretModel.getVisualPosition();
        }
        @Override
        public void update(AnActionEvent e) {
            //...
        }
    }
    

    2.5. Offset

    An absolute offset for a given caret position is accessible through CaretModel as well

    public class EditorAreaIllustration extends AnAction {
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            CaretModel caretModel = editor.getCaretModel();
            LogicalPosition logicalPosition = caretModel.getLogicalPosition();
            VisualPosition visualPosition = caretModel.getVisualPosition();
            int offset = caretModel.getOffset();
        }
        @Override
        public void update(AnActionEvent e) {
            //...
        }
    }
    

    2.6. Displaying position values

    To display the actual values of logical and visual positions we add an Messages.showInfoMessage() call that will show them in form of notification after the action is performed.

    public class EditorAreaIllustration extends AnAction {
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            CaretModel caretModel = editor.getCaretModel();
            LogicalPosition logicalPosition = caretModel.getLogicalPosition();
            VisualPosition visualPosition = caretModel.getVisualPosition();
            int offset = caretModel.getOffset();
            Messages.showInfoMessage(logicalPosition.toString() + "
    " +
                            visualPosition.toString() + "
    " +
                            "Offset: " + offset, "Caret Parameters Inside The Editor");
        }
        @Override
        public void update(AnActionEvent e) {
               //...
        }
    }
    

    Check out, compile, and run the Editor Basics Plugin, then move carets, invoke EditorAreaIllustration action, and see how logical and visual positions are related dependently on folding.

    Find the action in the context menu:

    Show coordinates action

    Perform the action to see caret positions:

    Show coordinates action

    3. Handling Editor Events

    The following set of tutorials is meant to be an introduction to actions activated by editor events. The IntelliJ Platform SDK provides a set of callbacks for handling events related to the Editor.

    Source code

    3.1. Handling keystrokes in the Editor

    To handle keystrokes and provide custom reactions interface TypedActionHandler may be used. Series of steps below shows how to change standard behaviour of the editor and make it react on typing differently instead of simply displaying a typed character in the editor area.

    3.1.2 Implementing TypedActionHandler

    First we need to implement an instance of TypedActionHandler:

    public class MyTypedHandler implements TypedActionHandler {
        @Override
        public void execute(@NotNull Editor editor, char c, @NotNull DataContext dataContext) {
        }
    }
    

    3.1.3. Implementing logic for handling keystrokes

    public void execute(@NotNull Editor editor, char c, @NotNull DataContext dataContext);

    method should contain the main logical part for handling keystrokes. It will be called every time a key is pressed. In the following example our typed handler is meant insert a string at the zero offset in the editor after a keystroke occurs:

    public class MyTypedHandler implements TypedActionHandler {
        @Override
        public void execute(@NotNull Editor editor, char c, @NotNull DataContext dataContext) {
            final Document document = editor.getDocument();
            Project project = editor.getProject();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    document.insertString(0, "Typed
    ");
                }
            };
            WriteCommandAction.runWriteCommandAction(project, runnable);
        }
    }
    

    3.1.4. Setting up TypedActionHandler

    To enable a custom implementation of TypedActionHandler in the plugin we need to create a new instance of it and pass to public TypedActionHandler setupHandler(TypedActionHandler handler); method of the TypedAction class. By doing it we replace the typing handler with the specified handler.

    public class EditorIllustration extends AnAction {
        static {
            final EditorActionManager actionManager = EditorActionManager.getInstance();
            final TypedAction typedAction = actionManager.getTypedAction();
            typedAction.setupHandler(new MyTypedHandler());
        }
    }
    

    After compiling and running the code snippet above typing in the editor will be handled with inserting an extra string at the 0 position.

    3.2. Working with EditorActionHandler

    Class EditorActionHandler.java stays for actions activated by keystrokes in the editor. Series of steps below show how access EditorActionManager and pass it actions to be executed. In this example we will use EditorActionHandler to insert one extra caret above the current caret if available.

    3.2.1. Pre-requirements

    Create an action:

    public class EditorHandlerIllustration extends AnAction {
        @Override                                        
        public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
        }
        @Override
        public void update(@NotNull final AnActionEvent anActionEvent) {
        }
    }
    

    Register action in plugin.xml:

    <actions>
        <action id="EditorBasics.EditorHandlerIllustration" class="org.jetbrains.tutorials.editor.basics.EditorHandlerIllustration" text="Editor Handler"
                description="Illustrates how to plug an action in">
          <add-to-group group-id="EditorPopupMenu" anchor="first"/>
        </action>
    </action>
    

    3.2.2. Setting visibility

    Our action should be visible only in case if the following conditions are met: there’s a project open, there’s an editor available, and there’s at least one caret active in the editor:

    public class EditorHandlerIllustration extends AnAction {
        @Override
        public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
        }
        @Override
        public void update(@NotNull final AnActionEvent anActionEvent) {
            final Project project = anActionEvent.getData(CommonDataKeys.PROJECT);
            final Editor editor = anActionEvent.getData(CommonDataKeys.EDITOR);
            anActionEvent.getPresentation().setVisible((project != null && editor != null && !editor.getCaretModel().getAllCarets().isEmpty()));
        }
    }
    

    3.2.3. Obtaining EditorActionHandler

    To manipulate with standard Editor’s actions first we need to obtain an instance of EditorActionHandler for the action we’d like to work with. Ih this case it will be an instance of CloneCaretActionHandler.

    public class EditorHandlerIllustration extends AnAction {
        @Override
        public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            EditorActionManager actionManager = EditorActionManager.getInstance();
            EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW);
        }
        @Override
        public void update(@NotNull final AnActionEvent anActionEvent) {
            //...
        }
    }
    

    3.2.4. Making EditorActionHandler execute actions

    To execute an action we need to call the public final void execute(@NotNull Editor editor, @Nullable final Caret contextCaret, final DataContext dataContext); method of a corresponding EditorActionHandler

    public class EditorHandlerIllustration extends AnAction {
        @Override
        public void actionPerformed(@NotNull AnActionEvent anActionEvent) {
            final Editor editor = anActionEvent.getRequiredData(CommonDataKeys.EDITOR);
            EditorActionManager actionManager = EditorActionManager.getInstance();
            EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW);
            actionHandler.execute(editor, editor.getCaretModel().getCurrentCaret(), anActionEvent.getDataContext());
        }
        @Override
        public void update(@NotNull final AnActionEvent anActionEvent) {
            //
        }
    }
    

    After compiling and running the following code sample, one extra caret will be placed in the editor below the current active caret.

    Source code

    Code Inspections

    This topic describes the comparing_references_inspection, a sample plugin that creates a custom inspection for Java code. In addition, the sample plugin contains a JUnit-based test.

    About Code Inspections

    The IntelliJ Platform provides tools designed for static code analysis (so called code inspections) that help you maintain and clean up your code without actually executing it. For more information, refer to Code Inspection in the IntelliJ IDEA Web Help. In IntelliJ IDEA you will find a set of built-in inspections that are grouped by their goals and sense.

    You can create custom inspections through the IntelliJ IDEA interface (see Creating Custom Inspections). Alternatively, you can develop a plugin to implement a custom inspection.

    Techniques Used

    The comparing_references_inspection sample plugin illustrates the use of the following techniques:

    Sample Plugin

    The comparingReferences sample plugin is available in the <%IntelliJ SDK Docs project%>/code_samples/comparing_references_inspection directory. When launched, this plugin adds the ’==’ or ‘!=’ instead of ‘equals()’ item to the Probable bugs node in the Inspections list.

    Running the Plugin

    To run the sample plugin

    1. Start IntelliJ IDEA and open the comparingReferences plugin project saved into the <%IntelliJ SDK Docs project%>/code_samples/comparing_references_inspection directory.
    2. Open the Project Structure dialog and ensure that the project settings are valid for your environment.
    3. If necessary, modify the Run/Debug Configurations and Run the plugin by choosing the **Run Run** on the main menu.

    Configuring the Plugin

    Once the plugin is launched, you can set the plugin options. You can specify the Java classes to be participated in the code inspection and the severity level of the found probable bugs.

    To configure the sample plugin

    1. On the IDEA main menu, choose **File Settings, and then under Project Settings, click Inspections**.
    2. In the list of the IntelliJ IDEA inspections, expand the Probable bugs node, and then click ’==’ or ‘!=’ instead of ‘equals()’.

    1. Under Options, you can specify the following plugin settings:
      • From the Severity list, select the severity level of probable bugs the plugin will find (such as Warning, Info, etc.)
      • In the text box under Severity, specify the semicolon separated list of Java classes to be participated in this code inspection.
    2. When finished, click OK.

    How does it work?

    The plugin inspects your code opened in the IntelliJ IDEA editor or the code you are typing. The plugin highlights the code fragments where two variables of the reference type are separated by == or != and proposes to replace this code fragment with .equals():

    In this example, the s1 and s2 are variables of the String type. Clicking Use equals() replaces

    return (s1==s2);
    

    with the code:

    return (s1.equals(s2));
    

    Testing the Plugin

    The sample plugin contains the TestThisPlugin Java class in the testSource/testPlugin package and the test data in the testData directory. This test adds two test cases to this plugin project. To run test cases, run the YourTest.test() or YourTest.test1() method, respectively.

    For detailed information about testing and all related procedures, refer to Testing and Testing Support in the IntelliJ IDEA Web Help.

    Code Intentions

    This topic describes the conditional_operator_intention, a sample plugin that adds a new intention action to the IntelliJ Platform Intentions list. In addition, the sample plugin contains a JUnit-based test.

    About Intention Actions

    The IntelliJ Platform analyzes your code and helps handle situations that may result in errors. When a possible problem is suspected, the IDE suggests an appropriate intention action, denoted with special icons. For more information, refer to Intention Actions in the IntelliJ IDEA Web Help.

    You can view a list of all available intention actions using the Intention List provided by the IDE.

    To display Intention List

    1. Open the Settings dialog box.
    2. Under IDE Settings, click Intentions. This displays the list of all intention actions currently available in IntelliJ IDEA.
      • The intention actions are grouped according to the areas of their use.
      • To enable/disable an intention action, select/deselect the check box to its left.

    Techniques Used

    The conditional_operator_intention sample plugin illustrates the use of the following techniques:

    Sample Plugin

    The ConditionalOperatorConverter sample plugin is available in the <%IntelliJ SDK Docs project%>/code_samples/conditional_operator_intention directory. When launched, this plugin adds the Convert ternary operator if statement item to the Conditional Operator node in the IDEA Intentions list:

    Running the Plugin

    To run the sample plugin

    1. Start IntelliJ IDEA and open the conditionalOperatorConvertor plugin project saved into the <%IntelliJ SDK Docs project%>/code_samples/conditional_operator_intention directory.
    2. Open the Project Structure dialog and ensure that the project settings are valid for your environment.
    3. If necessary, modify the Run/Debug Configurations and Run the plugin by choosing the Run on the main menu.

    How does it work?

    The plugin analyzes symbols under the cursor in your code opened in the IDEA editor. If the cursor is positioned on the “?” conditional operator, IntelliJ IDEA proposes to replace this conditional (ternary) operator with the “if-then-else” statement:

    In this example, the code:

    return (n>=0) ? n : -n;
    

    will be replaced with the code:

    if ((n>=0)) {
        return n;
    } else {
        return -n;
    }
    

    Testing the Plugin

    The sample plugin contains the YourTest Java class in the testSource/testPlugin/ package and the test data in the testData/ directory. To perform the plugin test, run the YourTest.test() method.

    For detailed information about testing and all related procedures, refer to Testing and Testing Support in the IntelliJ IDEA Web Help.

    Run Configurations

    These series of steps show how to register and implement a simple Run Configuration. Run Configurations are used to run internal and external processes from within IntelliJ Platform based products. To get familiar with the concept of a Run Configuration refer Run/Debug Configuration section of IntelliJ IDEA Web Help

    Pre-requirements

    Create an empty plugin project. See Creating a Plugin Project.

    1. Register a new ConfigurationType

    Add new configurationType extension to the plugin.xml

    <extensions defaultExtensionNs="com.intellij">
      <configurationType implementation="org.jetbrains.tutorials.run.configuration.DemoRunConfigurationType"/>
    </extensions>
    

    2. Implement ConfigurationType

    Implement ConfigurationType interface registered in the Step 1.

    public class DemoRunConfigurationType implements ConfigurationType {
        @Override
        public String getDisplayName() {
            return "Demo";
        }
    
        @Override
        public String getConfigurationTypeDescription() {
            return "Demo Run Configuration Type";
        }
    
        @Override
        public Icon getIcon() {
            return AllIcons.General.Information;
        }
    
        @NotNull
        @Override
        public String getId() {
            return "DEMO_RUN_CONFIGURATION";
        }
    
        @Override
        public ConfigurationFactory[] getConfigurationFactories() {
            return new ConfigurationFactory[]{new DemoConfigurationFactory(this)};
        }
    }
    

    3. Implement a ConfigurationFactory

    Implement a new ConfigurationFactory through which custom run configurations will be created.

    public class DemoConfigurationFactory extends ConfigurationFactory {
        private static final String FACTORY_NAME = "Demo configuration factory";
    
        protected DemoConfigurationFactory(ConfigurationType type) {
            super(type);
        }
    
        @Override
        public RunConfiguration createTemplateConfiguration(Project project) {
            return new DemoRunConfiguration(project, this, "Demo");
        }
    
        @Override
        public String getName() {
            return FACTORY_NAME;
        }
    }
    
    

    4. Implement a Run Configuration

    To make your changes visible from the UI, implement a new Run Configuration.

    Note: In most of the cases you can derive a custom Run Configuration class from the RunConfigurationBase. If you need to implement specific settings externalization rules and I/O behaviour, use RunConfiguration interface.

    public class DemoRunConfiguration extends RunConfigurationBase {
        protected DemoRunConfiguration(Project project, ConfigurationFactory factory, String name) {
            super(project, factory, name);
        }
    
        @NotNull
        @Override
        public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
            return new DemoSettingsEditor();
        }
    
        @Override
        public void checkConfiguration() throws RuntimeConfigurationException {
    
        }
    
        @Nullable
        @Override
        public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment) throws ExecutionException {
            return null;
        }
    }
    

    5. Create and Implement Run Configuration UI Form

    Create a new [UI form] that defines, how an inner part of the new Run Configuration should look like. Default Run Configuration will be looking like this:

    Default Run Configuration Look

    6. Bind the UI Form

    The UI Form should be bound with a Java class responsible for handling UI components logic.

    public class DemoSettingsEditor extends SettingsEditor<DemoRunConfiguration> {
        private JPanel myPanel;
        private LabeledComponent<ComponentWithBrowseButton> myMainClass;
    
        @Override
        protected void resetEditorFrom(DemoRunConfiguration demoRunConfiguration) {
    
        }
    
        @Override
        protected void applyEditorTo(DemoRunConfiguration demoRunConfiguration) throws ConfigurationException {
    
        }
    
        @NotNull
        @Override
        protected JComponent createEditor() {
            return myPanel;
        }
    
        private void createUIComponents() {
            myMainClass = new LabeledComponent<ComponentWithBrowseButton>();
            myMainClass.setComponent(new TextFieldWithBrowseButton());
        }
    }
    

    7. Compile and Run the Plugin

    Refer to Running and Debugging a Plugin.

    After going through the steps described above you can create a custom Run Configuration from your plugin.

    New Run Configuration Type

  • 相关阅读:
    Matlab中imagesc用法
    codevs 3160 最长公共子串(SAM)
    spoj 7258 SUBLEX(SAM,名次)
    spoj 1812 LCS2(SAM+DP)
    spoj 8222 Substrings(后缀自动机+DP)
    tyvj P1519 博彩游戏(AC自动机+DP滚动数组)
    bzoj 1030 [JSOI2007]文本生成器(AC自动机+DP)
    vijos P1459 车展(Treap,中位数)
    bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)
    bzoj 1483 [HNOI2009]梦幻布丁(链表+启发式合并)
  • 原文地址:https://www.cnblogs.com/liqiking/p/6804786.html
Copyright © 2011-2022 走看看