2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.openapi.vcs;
19 import com.intellij.lifecycle.PeriodicalTasksCloser;
20 import com.intellij.openapi.components.PersistentStateComponent;
21 import com.intellij.openapi.components.State;
22 import com.intellij.openapi.components.Storage;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.SimpleModificationTracker;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.util.io.URLUtil;
28 import com.intellij.util.xmlb.XmlSerializerUtil;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
40 @State(name = "IssueNavigationConfiguration", storages = @Storage("vcs.xml"))
41 public class IssueNavigationConfiguration extends SimpleModificationTracker
42 implements PersistentStateComponent<IssueNavigationConfiguration> {
43 private static final Logger LOG = Logger.getInstance(IssueNavigationConfiguration.class);
45 public static IssueNavigationConfiguration getInstance(Project project) {
46 return PeriodicalTasksCloser.getInstance().safeGetService(project, IssueNavigationConfiguration.class);
49 private List<IssueNavigationLink> myLinks = new ArrayList<>();
51 public List<IssueNavigationLink> getLinks() {
55 public void setLinks(final List<IssueNavigationLink> links) {
56 myLinks = new ArrayList<>(links);
57 incModificationCount();
60 public IssueNavigationConfiguration getState() {
64 public void loadState(IssueNavigationConfiguration state) {
65 XmlSerializerUtil.copyBean(state, this);
68 public static class LinkMatch implements Comparable {
69 private final TextRange myRange;
70 private final String myTargetUrl;
72 public LinkMatch(final TextRange range, final String targetUrl) {
74 myTargetUrl = targetUrl;
77 public TextRange getRange() {
81 public String getTargetUrl() {
85 public int compareTo(Object o) {
86 if (!(o instanceof LinkMatch)) {
89 return myRange.getStartOffset() - ((LinkMatch)o).getRange().getStartOffset();
93 public List<LinkMatch> findIssueLinks(CharSequence text) {
94 final List<LinkMatch> result = new ArrayList<>();
95 for (IssueNavigationLink link : myLinks) {
96 Pattern issuePattern = link.getIssuePattern();
97 Matcher m = issuePattern.matcher(text);
100 String replacement = issuePattern.matcher(m.group(0)).replaceFirst(link.getLinkRegexp());
101 addMatch(result, new TextRange(m.start(), m.end()), replacement);
103 catch (Exception e) {
104 LOG.debug("Malformed regex replacement. IssueLink: " + link + "; text: " + text, e);
108 Matcher m = URLUtil.URL_PATTERN.matcher(text);
110 addMatch(result, new TextRange(m.start(), m.end()), m.group());
112 Collections.sort(result);
116 private static void addMatch(final List<LinkMatch> result, final TextRange range, final String replacement) {
117 for (Iterator<LinkMatch> iterator = result.iterator(); iterator.hasNext(); ) {
118 LinkMatch oldMatch = iterator.next();
119 if (range.contains(oldMatch.getRange())) {
122 else if (oldMatch.getRange().contains(range)) {
126 result.add(new LinkMatch(range, replacement));